package org.ovirt.engine.api.restapi.resource; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.HashMap; import java.util.List; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import org.ovirt.engine.api.common.util.QueryHelper; import org.ovirt.engine.api.common.util.StatusUtils; import org.ovirt.engine.api.model.BaseResource; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.interfaces.SearchType; import org.ovirt.engine.core.common.queries.SearchParameters; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.compat.StringHelper; public abstract class AbstractBackendCollectionResource<R extends BaseResource, Q /* extends IVdcQueryable */> extends AbstractBackendResource<R, Q> { private static final String EXPECT_HEADER = "Expect"; private static final String BLOCKING_EXPECTATION = "201-created"; private static final String CREATION_STATUS_REL = "creation_status"; public static final String FROM_CONSTRAINT_PARAMETER = "from"; protected static final LogCompat LOG = LogFactoryCompat.getLog(AbstractBackendCollectionResource.class); protected AbstractBackendCollectionResource(Class<R> modelType, Class<Q> entityType, String... subCollections) { super(modelType, entityType, subCollections); } public Response remove(String id) { getEntity(id); //will throw 404 if entity not found. return performRemove(id); } protected abstract Response performRemove(String id); protected List<Q> getBackendCollection(SearchType searchType) { return getBackendCollection(searchType, QueryHelper.getConstraint(getUriInfo(), "", modelType)); } protected List<Q> getBackendCollection(SearchType searchType, String constraint) { return getBackendCollection(entityType, VdcQueryType.Search, getSearchParameters(searchType, constraint)); } private SearchParameters getSearchParameters(SearchType searchType, String constraint) { SearchParameters searchParams = new SearchParameters(constraint, searchType); HashMap<String, String> constraints = QueryHelper.getConstraints(getUriInfo().getQueryParameters(), FROM_CONSTRAINT_PARAMETER); if (constraints != null && constraints.containsKey(FROM_CONSTRAINT_PARAMETER) && !StringHelper.isNullOrEmpty(constraints.get(FROM_CONSTRAINT_PARAMETER))) { try { searchParams.setSearchFrom(Long.parseLong(constraints.get(FROM_CONSTRAINT_PARAMETER))); } catch (Exception ex) { LOG.error("Unwrapping of 'from' search parameter failed.", ex); } } return searchParams; } protected List<Q> getBackendCollection(VdcQueryType query, VdcQueryParametersBase queryParams) { return getBackendCollection(entityType, query, queryParams); } protected Response performCreation(VdcActionType task, VdcActionParametersBase taskParams, EntityIdResolver entityResolver) { VdcReturnValueBase createResult; try { createResult = backend.RunAction(task, sessionize(taskParams)); if (!createResult.getCanDoAction()) { throw new BackendFailureException(localize(createResult.getCanDoActionMessages())); } else if (!createResult.getSucceeded()) { throw new BackendFailureException(localize(createResult.getExecuteFailedMessages())); } } catch (Exception e) { return handleError(e, false); } R model = resolveCreated(createResult, entityResolver); Response response = null; if (createResult.getHasAsyncTasks()) { if (expectBlocking()) { awaitCompletion(createResult); // refresh model state model = resolveCreated(createResult, entityResolver); response = Response.created(URI.create(model.getHref())).entity(model).build(); } else { if (model==null) { response = Response.status(ACCEPTED_STATUS).build(); } else { handleAsynchrony(createResult, model); response = Response.status(ACCEPTED_STATUS).entity(model).build(); } } } else { if (model==null) { response = Response.status(ACCEPTED_STATUS).build(); } else { response = Response.created(URI.create(model.getHref())).entity(model).build(); } } return response; } protected boolean expectBlocking() { boolean expectBlocking = false; List<String> expect = httpHeaders.getRequestHeader(EXPECT_HEADER); if (expect != null && expect.size() > 0) { expectBlocking = expect.get(0).equalsIgnoreCase(BLOCKING_EXPECTATION); } return expectBlocking; } protected void handleAsynchrony(VdcReturnValueBase result, R model) { model.setCreationStatus(StatusUtils.create(getAsynchronousStatus(result))); linkSubResource(model, CREATION_STATUS_REL, asString(result.getTaskIdList())); } protected R resolveCreated(VdcReturnValueBase result, EntityIdResolver entityResolver) { try { Q created = entityResolver.resolve((Guid)result.getActionReturnValue()); return addLinks(populate(map(created), created)); } catch (Exception e) { // we tolerate a failure in the entity resolution // as the substantive action (entity creation) has // already succeeded e.printStackTrace(); return null; } } protected String asString(VdcReturnValueBase result) { Guid guid = (Guid)result.getActionReturnValue(); return guid != null ? guid.toString() : null; } protected void getEntity(String id) { try { Method method = getMethod(this.getClass(), SingleEntityResource.class); if (method==null) { LOG.error("Could not find sub-resource in the collection resource"); } else { Object entityResource = method.invoke(this, id); method = entityResource.getClass().getMethod("get"); if (method==null) { LOG.warn("Could not find GET method in " + entityResource.getClass().getName()); } else { method.invoke(entityResource); } } } catch (IllegalAccessException e) { LOG.error("Reflection Error", e); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof WebApplicationException) { throw((WebApplicationException)e.getTargetException()); } else { LOG.error("Reflection Error", e); } } catch (SecurityException e) { LOG.error("Reflection Error", e); } catch (NoSuchMethodException e) { LOG.error("Reflection Error", e); } } @SuppressWarnings("unchecked") private Method getMethod(Class<?> clazz, @SuppressWarnings("rawtypes") Class annotation) { for (Method m : clazz.getMethods()) { if (m.getAnnotation(annotation)!=null) { return m; } } return null; } }