blob: 04859d2cf0479063f4a4c16b026a57d9ed30dcfe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
*
******************************************************************************/
package org.eclipse.persistence.jpa.rs.resources.common;
import static org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller.mediaType;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.RelationalDescriptor;
import org.eclipse.persistence.indirection.ValueHolder;
import org.eclipse.persistence.internal.weaving.PersistenceWeavedRest;
import org.eclipse.persistence.jpa.rs.PersistenceContext;
import org.eclipse.persistence.jpa.rs.QueryParameters;
import org.eclipse.persistence.jpa.rs.util.IdHelper;
import org.eclipse.persistence.jpa.rs.util.JPARSLogger;
import org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller;
import org.eclipse.persistence.jpa.rs.util.list.SimpleHomogeneousList;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping;
/**
* @author gonural
*
*/
public abstract class AbstractEntityResource extends AbstractResource {
@SuppressWarnings({ "rawtypes" })
protected Response findAttribute(String version, String persistenceUnit, String type, String key, String attribute, HttpHeaders hh, UriInfo ui, URI baseURI) {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Map<String, String> discriminators = getMatrixParameters(ui, persistenceUnit);
Object id = IdHelper.buildId(app, type, key);
Object entity = app.findAttribute(discriminators, type, id, getQueryParameters(ui), attribute);
if (entity == null) {
JPARSLogger.fine("jpars_could_not_entity_for_attribute", new Object[] { type, key, attribute, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Boolean collectionContainsDomainObjects = collectionContainsDomainObjects(entity);
if (collectionContainsDomainObjects != null) {
if (collectionContainsDomainObjects.booleanValue()) {
return Response.ok(new StreamingOutputMarshaller(app, entity, hh.getAcceptableMediaTypes())).build();
} else {
// Classes derived from PersistenceWeavedRest class are already in the JAXB context and marshalled properly.
// Here, we will only need to deal with collection of classes that are not in the JAXB context, such as String, Integer...
//
// Jersey 1.2 introduced a new api JResponse to support this better, but in order to be able to work with
// older versions of Jersey, we will use our own wrapper.
return Response.ok(new StreamingOutputMarshaller(app, populateSimpleHomogeneousList((Collection) entity, attribute), hh.getAcceptableMediaTypes())).build();
}
}
return Response.ok(new StreamingOutputMarshaller(app, entity, hh.getAcceptableMediaTypes())).build();
}
protected Response find(String version, String persistenceUnit, String type, String key, HttpHeaders hh, UriInfo ui, URI baseURI) {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Map<String, String> discriminators = getMatrixParameters(ui, persistenceUnit);
Object id = IdHelper.buildId(app, type, key);
Object entity = app.find(discriminators, type, id, getQueryParameters(ui));
if (entity == null) {
JPARSLogger.fine("jpars_could_not_entity_for_key", new Object[] { type, key, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
} else {
return Response.ok(new StreamingOutputMarshaller(app, entity, hh.getAcceptableMediaTypes())).build();
}
}
@SuppressWarnings("rawtypes")
protected Response create(String version, String persistenceUnit, String type, HttpHeaders hh, UriInfo uriInfo, URI baseURI, InputStream in) throws JAXBException {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
ClassDescriptor descriptor = app.getDescriptor(type);
if (descriptor == null) {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Object entity = null;
try {
entity = app.unmarshalEntity(type, mediaType(hh.getAcceptableMediaTypes()), in);
} catch (JAXBException e) {
JPARSLogger.fine("exception_while_unmarhalling_entity", new Object[] { type, persistenceUnit, e.toString() });
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
// maintain idempotence on PUT by disallowing sequencing
AbstractDirectMapping sequenceMapping = descriptor.getObjectBuilder().getSequenceMapping();
if (sequenceMapping != null) {
Object value = sequenceMapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (descriptor.getObjectBuilder().isPrimaryKeyComponentInvalid(value, descriptor.getPrimaryKeyFields().indexOf(descriptor.getSequenceNumberField()))
|| descriptor.getSequence().shouldAlwaysOverrideExistingValue()) {
JPARSLogger.fine("jpars_put_not_idempotent", new Object[] { type, persistenceUnit });
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
}
// maintain idempotence on PUT by disallowing sequencing in relationships
List<DatabaseMapping> mappings = descriptor.getMappings();
if ((mappings != null) && (!mappings.isEmpty())) {
for (DatabaseMapping mapping : mappings) {
if (mapping instanceof ForeignReferenceMapping) {
ForeignReferenceMapping fkMapping = (ForeignReferenceMapping) mapping;
if ((fkMapping.isCascadePersist()) || (fkMapping.isCascadeMerge())) {
ClassDescriptor referenceDescriptor = fkMapping.getReferenceDescriptor();
if (referenceDescriptor != null) {
if (referenceDescriptor instanceof RelationalDescriptor) {
RelationalDescriptor relDesc = (RelationalDescriptor) referenceDescriptor;
AbstractDirectMapping relSequenceMapping = relDesc.getObjectBuilder().getSequenceMapping();
if (relSequenceMapping != null) {
Object value = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (value != null) {
if (value instanceof ValueHolder) {
ValueHolder holder = (ValueHolder) value;
if (holder != null) {
Object obj = holder.getValue();
if (obj != null) {
JPARSLogger.fine("jpars_put_not_idempotent", new Object[] { type, persistenceUnit });
return Response.status(Status.BAD_REQUEST).build();
}
}
} else if (value instanceof Collection) {
if (!(((Collection) value).isEmpty())) {
JPARSLogger.fine("jpars_put_not_idempotent", new Object[] { type, persistenceUnit });
return Response.status(Status.BAD_REQUEST).build();
}
}
}
}
}
}
}
}
}
}
// No sequencing in relationships, we can create the object now...
app.create(getMatrixParameters(uriInfo, persistenceUnit), entity);
ResponseBuilder rb = Response.status(Status.CREATED);
rb.entity(new StreamingOutputMarshaller(app, entity, hh.getAcceptableMediaTypes()));
return rb.build();
}
protected Response update(String version, String persistenceUnit, String type, HttpHeaders hh, UriInfo uriInfo, URI baseURI, InputStream in) {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Object entity = null;
try {
entity = app.unmarshalEntity(type, mediaType(hh.getAcceptableMediaTypes()), in);
} catch (JAXBException e) {
JPARSLogger.fine("exception_while_unmarhalling_entity", new Object[] { type, persistenceUnit, e.toString() });
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
entity = app.merge(getMatrixParameters(uriInfo, persistenceUnit), entity);
return Response.ok(new StreamingOutputMarshaller(app, entity, hh.getAcceptableMediaTypes())).build();
}
protected Response setOrAddAttribute(String version, String persistenceUnit, String type, String key, String attribute, HttpHeaders hh, UriInfo ui, URI baseURI, InputStream in) {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Object id = IdHelper.buildId(app, type, key);
Object entity = null;
String partner = getRelationshipPartner(getMatrixParameters(ui, attribute), getQueryParameters(ui));
try {
ClassDescriptor descriptor = app.getDescriptor(type);
DatabaseMapping mapping = (DatabaseMapping) descriptor.getMappingForAttributeName(attribute);
if (!mapping.isForeignReferenceMapping()) {
JPARSLogger.fine("jpars_could_find_appropriate_mapping_for_update", new Object[] { attribute, type, key, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
entity = app.unmarshalEntity(((ForeignReferenceMapping) mapping).getReferenceDescriptor().getAlias(), mediaType(hh.getAcceptableMediaTypes()), in);
} catch (JAXBException e) {
JPARSLogger.fine("exception_while_unmarhalling_entity", new Object[] { type, persistenceUnit, e.toString() });
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Object result = app.updateOrAddAttribute(getMatrixParameters(ui, persistenceUnit), type, id, getQueryParameters(ui), attribute, entity, partner);
if (result == null) {
JPARSLogger.fine("jpars_could_not_update_attribute", new Object[] { attribute, type, key, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
} else {
return Response.ok(new StreamingOutputMarshaller(app, result, hh.getAcceptableMediaTypes())).build();
}
}
protected Response removeAttributeInternal(String version, String persistenceUnit, String type, String key, String attribute, HttpHeaders hh, UriInfo ui) {
String listItemId = null;
Map<String, String> matrixParams = getMatrixParameters(ui, attribute);
Map<String, Object> queryParams = getQueryParameters(ui);
if ((queryParams != null) && (!queryParams.isEmpty())) {
listItemId = (String) queryParams.get(QueryParameters.JPARS_LIST_ITEM_ID);
}
String partner = getRelationshipPartner(matrixParams, queryParams);
PersistenceContext app = getPersistenceContext(persistenceUnit, ui.getBaseUri(), version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
if ((attribute == null) && (listItemId == null)) {
return Response.status(Status.BAD_REQUEST).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Object id = IdHelper.buildId(app, type, key);
ClassDescriptor descriptor = app.getDescriptor(type);
DatabaseMapping mapping = (DatabaseMapping) descriptor.getMappingForAttributeName(attribute);
if (!mapping.isForeignReferenceMapping()) {
JPARSLogger.fine("jpars_could_find_appropriate_mapping_for_update", new Object[] { attribute, type, key, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Map<String, String> discriminators = getMatrixParameters(ui, persistenceUnit);
Object entity = app.find(discriminators, type, id, getQueryParameters(ui));
Object result = app.removeAttribute(getMatrixParameters(ui, persistenceUnit), type, id, attribute, listItemId, entity, partner);
if (result == null) {
JPARSLogger.fine("jpars_could_not_update_attribute", new Object[] { attribute, type, key, persistenceUnit });
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
} else {
return Response.ok(new StreamingOutputMarshaller(app, result, hh.getAcceptableMediaTypes())).build();
}
}
protected Response delete(String version, String persistenceUnit, String type, String key, UriInfo ui, HttpHeaders hh, URI baseURI) {
PersistenceContext app = getPersistenceContext(persistenceUnit, baseURI, version, null);
if (app == null || app.getClass(type) == null) {
if (app == null) {
JPARSLogger.fine("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit });
} else {
JPARSLogger.fine("jpars_could_not_find_class_in_persistence_unit", new Object[] { type, persistenceUnit });
}
return Response.status(Status.NOT_FOUND).type(StreamingOutputMarshaller.getResponseMediaType(hh)).build();
}
Map<String, String> discriminators = getMatrixParameters(ui, persistenceUnit);
Object id = IdHelper.buildId(app, type, key);
app.delete(discriminators, type, id);
return Response.ok().build();
}
@SuppressWarnings("rawtypes")
private Boolean collectionContainsDomainObjects(Object object) {
if (!(object instanceof Collection)) {
return null;
}
Collection collection = (Collection) object;
for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
Object collectionItem = iterator.next();
if (PersistenceWeavedRest.class.isAssignableFrom(collectionItem.getClass())) {
return true;
}
}
return false;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private SimpleHomogeneousList populateSimpleHomogeneousList(Collection collection, String attributeName) {
SimpleHomogeneousList simpleList = new SimpleHomogeneousList();
List<JAXBElement> items = new ArrayList<JAXBElement>();
for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
Object collectionItem = iterator.next();
if (!(PersistenceWeavedRest.class.isAssignableFrom(collectionItem.getClass()))) {
JAXBElement jaxbElement = new JAXBElement(new QName(attributeName), collectionItem.getClass(), collectionItem);
items.add(jaxbElement);
}
}
simpleList.setItems(items);
return simpleList;
}
}