package io.github.ibuildthecloud.gdapi.request.resource.impl; import io.github.ibuildthecloud.gdapi.condition.Condition; import io.github.ibuildthecloud.gdapi.context.ApiContext; import io.github.ibuildthecloud.gdapi.factory.SchemaFactory; import io.github.ibuildthecloud.gdapi.id.IdFormatter; import io.github.ibuildthecloud.gdapi.model.Action; import io.github.ibuildthecloud.gdapi.model.Collection; import io.github.ibuildthecloud.gdapi.model.ListOptions; import io.github.ibuildthecloud.gdapi.model.Pagination; import io.github.ibuildthecloud.gdapi.model.Resource; import io.github.ibuildthecloud.gdapi.model.Schema; import io.github.ibuildthecloud.gdapi.model.Schema.Method; import io.github.ibuildthecloud.gdapi.model.impl.CollectionImpl; import io.github.ibuildthecloud.gdapi.model.impl.WrappedResource; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.request.resource.ResourceManager; import io.github.ibuildthecloud.gdapi.request.resource.ResourceManagerLocator; import io.github.ibuildthecloud.gdapi.response.ResourceOutputFilter; import io.github.ibuildthecloud.gdapi.response.ResourceOutputFilterManager; import io.github.ibuildthecloud.gdapi.url.UrlBuilder; import io.github.ibuildthecloud.gdapi.util.RequestUtils; import io.github.ibuildthecloud.gdapi.util.TypeUtils; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.WeakHashMap; import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; public abstract class AbstractBaseResourceManager implements ResourceManager { private static final String DEFAULT_CRITERIA = " _defaultCriteria"; Map<String, Map<String, String>> linksCache = Collections.synchronizedMap(new WeakHashMap<String, Map<String, String>>()); Set<Class<?>> resourcesToCreate = new HashSet<Class<?>>(); protected ResourceManagerLocator locator; protected ResourceOutputFilterManager outputFilterManager; protected Object authorize(Object object) { return object; } protected void addResourceToCreateResponse(Class<?> clz) { this.resourcesToCreate.add(clz); } @Override public final Object getById(String type, String id, ListOptions options) { return authorize(getByIdInternal(type, id, options)); } protected Object getByIdInternal(String type, String id, ListOptions options) { Map<Object, Object> criteria = getDefaultCriteria(true, false, type); criteria.put(TypeUtils.ID_FIELD, id); return getFirstFromList(listInternal(ApiContext.getSchemaFactory(), type, criteria, options)); } @Override public final Object list(String type, ApiRequest request) { return list(type, new LinkedHashMap<Object, Object>(request.getConditions()), new ListOptions(request)); } @Override public final List<?> list(String type, Map<Object, Object> criteria, ListOptions options) { if (!isDefaultCriteria(criteria)) { criteria = mergeCriteria(criteria, getDefaultCriteria(false, false, type)); } Object result = authorize(listInternal(ApiContext.getSchemaFactory(), type, criteria, options)); return RequestUtils.toList(result); } protected Map<Object, Object> mergeCriteria(Map<Object, Object> criteria, Map<Object, Object> other) { if (criteria == null) { criteria = new LinkedHashMap<Object, Object>(); } for (Map.Entry<Object, Object> entry : other.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); Object existing = criteria.get(key); if (existing instanceof List) { List<Object> newCondition = new ArrayList<Object>(); newCondition.add(value); newCondition.addAll((List<?>)existing); criteria.put(key, newCondition); } else if (existing == null) { criteria.put(key, value); } else { criteria.put(key, Arrays.asList(value, existing)); } } return criteria; } protected abstract Object listInternal(SchemaFactory schemaFactory, String type, Map<Object, Object> criteria, ListOptions options); @Override public final Object create(String type, ApiRequest request) { return authorize(createInternal(type, request)); } protected abstract Object createInternal(String type, ApiRequest request); @Override public final Object update(String type, String id, ApiRequest request) { Object object = getById(type, id, new ListOptions(request)); if (object == null) { return null; } return updateInternal(type, id, object, request); } protected abstract Object updateInternal(String type, String id, Object obj, ApiRequest request); @Override public final Object delete(String type, String id, ApiRequest request) { Object object = getById(type, id, new ListOptions(request)); if (object == null) { return null; } return deleteInternal(type, id, object, request); } protected abstract Object deleteInternal(String type, String id, Object obj, ApiRequest request); @Override public final Object getLink(String type, String id, String link, ApiRequest request) { return authorize(getLinkInternal(type, id, link, request)); } protected abstract Object getLinkInternal(String type, String id, String link, ApiRequest request); protected Map<Object, Object> getDefaultCriteria(boolean byId, boolean byLink, String type) { Map<Object, Object> result = new HashMap<Object, Object>(); result.put(DEFAULT_CRITERIA, true); return result; } protected boolean isDefaultCriteria(Map<Object, Object> criteria) { return criteria != null && criteria.containsKey(DEFAULT_CRITERIA); } protected Object getMarker(Pagination pagination) { if (pagination == null) { return null; } String marker = pagination.getMarker(); if (StringUtils.isBlank(marker)) { return null; } if (marker.charAt(0) == 'm') { return marker.substring(1); } Object obj = ApiContext.getContext().getIdFormatter().parseId(marker); if (obj instanceof Long) { return obj; } else if (obj != null) { try { return new Long(obj.toString()); } catch (NumberFormatException e) { // ignore } } return null; } @Override public Collection convertResponse(List<?> list, ApiRequest request) { return createCollection(list, request); } @Override public Resource convertResponse(Object obj, ApiRequest request) { Resource resource = createResource(obj, ApiContext.getContext().getIdFormatter(), request); ResourceOutputFilter filter = outputFilterManager.getOutputFilter(resource); if (filter != null) { resource = filter.filter(request, obj, resource); } return resource; } protected Collection createCollection(List<?> list, ApiRequest request) { CollectionImpl collection = new CollectionImpl(); if (request != null) { String collectionType = getCollectionType(list, request); collection.setResourceType(collectionType); Schema schema = request.getSchemaFactory().getSchema(collectionType); if (schema != null && schema.getChildren().size() > 0) { UrlBuilder urlBuilder = ApiContext.getUrlBuilder(); Map<String, URL> createTypes = new TreeMap<String, URL>(); if (schema.getCollectionMethods().contains(Method.POST.toString())) { createTypes.put(schema.getId(), urlBuilder.resourceCollection(collectionType)); } for (String childType : schema.getChildren()) { schema = request.getSchemaFactory().getSchema(childType); if (schema != null && schema.getCollectionMethods().contains(Method.POST.toString())) { createTypes.put(schema.getId(), urlBuilder.resourceCollection(schema.getId())); } } if (createTypes.size() > 0) { collection.setCreateTypes(createTypes); } } } collection.setCreateDefaults(request.getCreateDefaults()); addSort(collection, request); addPagination(list, collection, request); addFilters(collection, request); for (Object obj : list) { Resource resource = convertResponse(obj, request); if (resource != null) { collection.getData().add(resource); if (collection.getResourceType() == null) { collection.setResourceType(resource.getType()); } } } return collection; } protected String getCollectionType(List<?> list, ApiRequest request) { return request.getType(); } protected void addFilters(CollectionImpl collection, ApiRequest request) { Schema schema = request.getSchemaFactory().getSchema(collection.getResourceType()); Map<String, List<Condition>> conditions = new TreeMap<String, List<Condition>>(request.getConditions()); for (String key : schema.getCollectionFilters().keySet()) { if (!conditions.containsKey(key)) { conditions.put(key, null); } } collection.setFilters(conditions); } protected void addPagination(List<?> list, CollectionImpl collection, ApiRequest request) { Pagination pagination = request.getPagination(); if (pagination == null) { return; } collection.setPagination(pagination.getResponse()); } protected void addSort(CollectionImpl collection, ApiRequest request) { UrlBuilder urlBuilder = ApiContext.getUrlBuilder(); Set<String> sortLinks = getSortLinks(request.getSchemaFactory(), collection.getResourceType()); Map<String, URL> sortLinkMap = new TreeMap<String, URL>(); for (String sortLink : sortLinks) { URL sortUrl = urlBuilder.sort(sortLink); if (sortUrl != null) { sortLinkMap.put(sortLink, sortUrl); } } collection.setSortLinks(sortLinkMap); collection.setSort(request.getSort()); } protected Set<String> getSortLinks(SchemaFactory schemaFactory, String type) { String key = schemaFactory.getId() + ":sortlinks:" + type; Map<String, String> links = linksCache.get(key); if (links != null) return links.keySet(); links = new HashMap<String, String>(); Schema schema = schemaFactory.getSchema(type); if (schema == null) { return Collections.emptySet(); } for (String name : schema.getCollectionFilters().keySet()) { links.put(name, name); } linksCache.put(key, links); return links.keySet(); } protected Resource createResource(Object obj, IdFormatter idFormatter, ApiRequest apiRequest) { if (obj == null) return null; if (obj instanceof Resource) return (Resource)obj; SchemaFactory schemaFactory = apiRequest == null ? ApiContext.getSchemaFactory() : apiRequest.getSchemaFactory(); if (resourcesToCreate.size() > 0 && !resourcesToCreate.contains(obj.getClass())) { String type = schemaFactory.getSchemaName(obj.getClass()); ResourceManager rm = locator.getResourceManagerByType(type); if (rm != null) { return rm.convertResponse(obj, apiRequest); } } Schema schema = getSchemaForDisplay(schemaFactory, obj); if (schema == null) { return null; } Resource resource = constructResource(idFormatter, schemaFactory, schema, obj, apiRequest); addLinks(obj, schemaFactory, schema, resource); addActions(obj, schemaFactory, schema, resource); return resource; } protected Schema getSchemaForDisplay(SchemaFactory schemaFactory, Object obj) { return schemaFactory.getSchema(obj.getClass()); } protected void addActions(Object obj, SchemaFactory schemaFactory, Schema schema, Resource resource) { Map<String, Action> actions = schema.getResourceActions(); if (actions == null || actions.size() == 0) { return; } UrlBuilder urlBuilder = ApiContext.getUrlBuilder(); for (String name : actions.keySet()) { resource.getActions().put(name, urlBuilder.actionLink(resource, name)); } } protected Resource constructResource(IdFormatter idFormatter, SchemaFactory schemaFactory, Schema schema, Object obj, ApiRequest apiRequest) { return new WrappedResource(idFormatter, schemaFactory, schema, obj, apiRequest.getMethod()); } protected void addLinks(Object obj, SchemaFactory schemaFactory, Schema schema, Resource resource) { Map<String, URL> links = resource.getLinks(); for (Map.Entry<String, String> entry : getLinks(schemaFactory, resource).entrySet()) { String linkName = entry.getKey(); String propName = entry.getValue(); URL link = ApiContext.getUrlBuilder().resourceLink(resource, linkName); if (link == null) { continue; } if (propName != null && resource.getFields().get(propName) == null) { continue; } links.put(linkName, link); } } protected Map<String, String> getLinks(SchemaFactory schemaFactory, Resource resource) { return new HashMap<String, String>(); } public static Object getFirstFromList(Object obj) { if (obj instanceof Collection) { return getFirstFromList(((Collection)obj).getData()); } if (obj instanceof List) { List<?> list = (List<?>)obj; return list.size() > 0 ? list.get(0) : null; } return null; } @Override public boolean handleException(Throwable t, ApiRequest request) { return false; } @Override public final Object resourceAction(String type, ApiRequest request) { Object resource = getById(type, request.getId(), new ListOptions()); if (resource == null) { return null; } return resourceActionInternal(resource, request); } protected abstract Object resourceActionInternal(Object obj, ApiRequest request); @Override public final Object collectionAction(String type, ApiRequest request) { Object resources = list(type, request); if (resources == null) { return null; } return collectionActionInternal(resources, request); } protected abstract Object collectionActionInternal(Object resources, ApiRequest request); public ResourceManagerLocator getLocator() { return locator; } @Inject public void setLocator(ResourceManagerLocator locator) { this.locator = locator; } public ResourceOutputFilterManager getOutputFilterManager() { return outputFilterManager; } @Inject public void setOutputFilterManager(ResourceOutputFilterManager outputFilterManager) { this.outputFilterManager = outputFilterManager; } }