NOTE: Please ignore my use of MultivaluedMap
instead of multiple vargs String...args.
Is there a standard way in java of doing this?
What I have is a resource, that is returned from a remote server. But before each query, the remote connection must be open, and after the returns are returned - it must be closed.
So a natural way of doing this is something like:
Connection c = config.configureConnection();
c.open(); //open
List<Car> cars;
try{
cars = c.getCars();
}finally{
c.close(); //close
}
Now I want to implement something that operates on the level of the resources themselves, without worrying about connection, for example:
List<Car> cars = new CarResource().all(); //opens and closes connection
The way I am currently doing it is by having one abstract class, AbstractQueriable call abstract methods query(String ...args) and query(int id), which any class extending it must implement.
The AbstractQuerieable implements the Queriable interface, which makes it expose the three public methods filter(String ...args), all() and get(int id) - which are the public facing methods.
Here is the Queriable interface:
public interface Queriable <T>{
public T get(String id);
/** Simply returns all resources */
public Collection<T> all();
public Collection<T> filter(MultivaluedMap<String, String> args);
}
here is the AbstractQueriable class that implements it:
public abstract class AbstractQueriable<T> implements Queriable<T> {
@Override
public final T get(String id) {
setup();
try {
return query(id);
} finally {
cleanup();
}
}
@Override
public final Collection<T> filter(MultivaluedMap<String, String> args) {
setup();
try {
return query(args);
} finally {
cleanup();
}
}
/**
* Returns all resources.
*
* This is a convenience method that is equivalent to passing an empty
* arguments list to the filter function.
*
* @return The collection of all resources if possible
*/
@Override
public final Collection<T> all() {
return filter(null);
}
/**
* Queries for a resource by id.
*
* @param id
* id of the resource to return
* @return
*/
protected abstract T query(String id);
/**
* Queries for a resource by given arguments.
*
* @param args
* Map of arguments, where each key is the argument name, and the
* corresponing values are the values
* @return The collection of resources found
*/
protected abstract Collection<T> query(MultivaluedMap<String, String> args);
private void cleanup() {
Repository.close();
}
private void setup() {
Repository.open();
}
and finally my resource, which I want to use in the code, must extend the AbstractQueriable class, for example (please note that the details of these methods are not important):
public class CarRepositoryResource extends AbstractQueriable<Car> {
@Override
protected Car query(String id) {
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
params.add("CarID", id);
// Delegate the query to the parametarized version
Collection<cars> cars = query(params);
if (cars == null || cars.size() == 0) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
if (cars.size() > 1) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return cars.iterator().next();
}
@Override
protected Collection<Car> query(MultivaluedMap<String, String> params) {
Collection<Car> cars = new ArrayList<Car>();
Response response = Repository.getConnection().doQuery("Car");
while (response.next()) {
Returned returned = response.getResult();
if (returned != null) {
cars.add(returned);
}
}
return cars;
}
}
which finally, I can use in my code:
Collection<Car> cars = new CarRepositoryResource().all();
//... display cars to the client etc...
There are a few things I don't like about this kind of setup:
- I must instantiate a new instance of my "CarRepositoryResource" every time I do a query.
- The method names "query", while internal and private, are still confusing and clunky.
- I am not sure if there is a better pattern or framework out there.
The connection that I am using does not support/implement the JDBC api and is not sql-based.