/******************************************************************************* | |
* Copyright (c) 2011 Oracle. 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. | |
* | |
* Contributors: | |
* dclarke/tware - initial | |
******************************************************************************/ | |
package org.eclipse.persistence.jpa.rs; | |
import static org.eclipse.persistence.jaxb.JAXBContext.MEDIA_TYPE; | |
import static org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller.mediaType; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.logging.Logger; | |
import javax.annotation.PreDestroy; | |
import javax.ejb.EJB; | |
import javax.ejb.Singleton; | |
import javax.ws.rs.Consumes; | |
import javax.ws.rs.GET; | |
import javax.ws.rs.POST; | |
import javax.ws.rs.PUT; | |
import javax.ws.rs.Path; | |
import javax.ws.rs.PathParam; | |
import javax.ws.rs.Produces; | |
import javax.ws.rs.WebApplicationException; | |
import javax.ws.rs.core.Context; | |
import javax.ws.rs.core.HttpHeaders; | |
import javax.ws.rs.core.MediaType; | |
import javax.ws.rs.core.PathSegment; | |
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.StreamingOutput; | |
import javax.ws.rs.core.UriInfo; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBElement; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.bind.Unmarshaller; | |
import javax.xml.transform.stream.StreamSource; | |
import org.eclipse.persistence.config.PersistenceUnitProperties; | |
import org.eclipse.persistence.dynamic.DynamicEntity; | |
import org.eclipse.persistence.jpa.rs.metadata.DatabaseMetadataStore; | |
import org.eclipse.persistence.jpa.rs.util.IdHelper; | |
import org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller; | |
import com.sun.jersey.core.spi.factory.ResponseBuilderImpl; | |
/** | |
* JAX-RS application interface JPA-RS | |
* | |
* @author dclarke | |
* @since EclipseLink 2.4.0 | |
*/ | |
@Singleton | |
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) | |
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) | |
@Path("/") | |
public class Service { | |
static final Logger logger = Logger.getLogger("AppService"); | |
private PersistenceFactory factory; | |
public PersistenceFactory getPersistenceFactory() { | |
return factory; | |
} | |
@EJB | |
public void setPersistenceFactory(PersistenceFactory factory) { | |
this.factory = factory; | |
} | |
@POST | |
@Path("/") | |
public Response start(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in){ | |
ResponseBuilder rb = new ResponseBuilderImpl(); | |
try{ | |
factory.setMetadataStore(new DatabaseMetadataStore()); | |
List<String> datasourceValues = hh.getRequestHeader("datasourceName"); | |
Map<String, Object> properties = new HashMap<String, Object>(); | |
if (datasourceValues != null && datasourceValues.size() > 0){ | |
properties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, datasourceValues.get(0)); | |
} | |
factory.getMetadataStore().setProperties(properties); | |
} catch (Exception e){ | |
rb.status(Status.NOT_FOUND); | |
} | |
rb.status(Status.OK); | |
return rb.build(); | |
} | |
@POST | |
@Path("{context}") | |
public Response bootstrap(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in){ | |
ResponseBuilder rb = new ResponseBuilderImpl(); | |
String urlString = getURL(hh); | |
PersistenceContext persistenceContext = null; | |
boolean replace = false; | |
List<String> replaceValues = hh.getRequestHeader("replace"); | |
if (replaceValues != null && replaceValues.size() > 0){ | |
replace = Boolean.getBoolean(replaceValues.get(0)); | |
} | |
try{ | |
if (urlString != null){ | |
URL url = new URL(urlString); | |
persistenceContext = factory.bootstrapPersistenceContext(persistenceUnit, url, new HashMap<String, Object>(), replace); | |
} else { | |
persistenceContext = factory.bootstrapPersistenceContext(persistenceUnit, in, new HashMap<String, Object>(), replace); | |
} | |
} catch (Exception e){ | |
rb.status(Status.NOT_FOUND); | |
} | |
if (persistenceContext != null){ | |
rb.status(Status.OK); | |
} | |
return rb.build(); | |
} | |
@GET | |
@Path("{context}/entity/{type}") | |
public Response find(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, @Context UriInfo ui) { | |
PersistenceContext app = get(persistenceUnit); | |
Object id = IdHelper.buildId(app, type, ui.getQueryParameters()); | |
Object entity = app.find(getTenantId(hh), type, id); | |
ResponseBuilder rb = new ResponseBuilderImpl(); | |
if (entity == null) { | |
rb.status(Status.NOT_FOUND); | |
} else { | |
rb.status(Status.OK); | |
rb.entity(new StreamingOutputMarshaller(app.getJAXBContext(), entity, hh.getAcceptableMediaTypes())); | |
} | |
return rb.build(); | |
} | |
@PUT | |
@Path("{context}/entity/{type}") | |
public Response create(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in) { | |
PersistenceContext app = get(persistenceUnit); | |
DynamicEntity entity = unmarshalEntity(app, type, getTenantId(hh), mediaType(hh.getAcceptableMediaTypes()), in); | |
app.create(getTenantId(hh), entity); | |
ResponseBuilder rb = new ResponseBuilderImpl(); | |
rb.status(Status.OK); | |
rb.entity(new StreamingOutputMarshaller(app.getJAXBContext(), entity, hh.getAcceptableMediaTypes())); | |
return rb.build(); | |
} | |
@POST | |
@Path("{context}/entity/{type}") | |
public StreamingOutput update(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in) { | |
PersistenceContext app = get(persistenceUnit); | |
String tenantId = getTenantId(hh); | |
MediaType contentType = mediaType(hh.getRequestHeader(HttpHeaders.CONTENT_TYPE)); | |
DynamicEntity entity = unmarshalEntity(app, type, tenantId, contentType, in); | |
entity = app.merge(type, tenantId, entity); | |
JAXBContext context = app.getJAXBContext(); | |
return new StreamingOutputMarshaller(context, entity, hh.getAcceptableMediaTypes()); | |
} | |
@GET | |
@Path("{context}/query/{name}") | |
public StreamingOutput namedQuery(@PathParam("context") String persistenceUnit, @PathParam("name") String name, @Context HttpHeaders hh, @Context UriInfo ui) { | |
PersistenceContext app = get(persistenceUnit); | |
Object result = app.query(name, Service.getParameterMap(ui), false); | |
JAXBContext context = app.getJAXBContext(); | |
return new StreamingOutputMarshaller(context, result, hh.getAcceptableMediaTypes()); | |
} | |
@GET | |
@Path("{context}/singleResultQuery/{name}") | |
@Produces(MediaType.WILDCARD) | |
public StreamingOutput namedQuerySingleResult(@PathParam("context") String persistenceUnit, @PathParam("name") String name, @Context HttpHeaders hh, @Context UriInfo ui) { | |
PersistenceContext app = get(persistenceUnit); | |
Object result = app.query(name, Service.getParameterMap(ui), true); | |
JAXBContext context = app.getJAXBContext(); | |
return new StreamingOutputMarshaller(context, result, hh.getAcceptableMediaTypes()); | |
} | |
/** | |
* This method has been temporarily added to allow processing of either query or matrix parameters | |
* When the final protocol is worked out, it should be removed or altered. | |
* | |
* Here we check for query parameters and if they don't exist, we get the matrix parameters. | |
* @param info | |
* @return | |
*/ | |
private static Map<String, Object> getParameterMap(UriInfo info){ | |
Map<String, Object> parameters = new HashMap<String, Object>(); | |
PathSegment pathSegment = info.getPathSegments().get(info.getPathSegments().size() - 1); | |
for(Entry<String, List<String>> entry : pathSegment.getMatrixParameters().entrySet()) { | |
parameters.put(entry.getKey(), entry.getValue().get(0)); | |
} | |
return parameters; | |
} | |
@GET | |
@Path("{context}/query") | |
public StreamingOutput adhocQuery(@PathParam("context") String persistenceUnit, @Context HttpHeaders hh, @Context UriInfo ui) { | |
throw new WebApplicationException(Status.SERVICE_UNAVAILABLE); | |
} | |
private PersistenceContext get(String persistenceUnit) { | |
PersistenceContext app = getPersistenceFactory().getPersistenceContext(persistenceUnit); | |
if (app == null) { | |
throw new WebApplicationException(Status.NOT_FOUND); | |
} | |
return app; | |
} | |
private String getTenantId(HttpHeaders hh) { | |
List<String> tenantIdValues = hh.getRequestHeader("tenant-id"); | |
if (tenantIdValues == null || tenantIdValues.isEmpty()) { | |
return null; | |
} | |
if (tenantIdValues.size() != 1) { | |
throw new WebApplicationException(Status.BAD_REQUEST); | |
} | |
return tenantIdValues.get(0); | |
} | |
private DynamicEntity unmarshalEntity(PersistenceContext app, String type, String tenantId, MediaType acceptedMedia, InputStream in) { | |
Unmarshaller unmarshaller; | |
try { | |
unmarshaller = app.getJAXBContext().createUnmarshaller(); | |
unmarshaller.setProperty(MEDIA_TYPE, acceptedMedia.toString()); | |
JAXBElement<?> element = unmarshaller.unmarshal(new StreamSource(in), app.getClass(type)); | |
return (DynamicEntity) element.getValue(); | |
} catch (JAXBException e) { | |
throw new WebApplicationException(Status.BAD_REQUEST); | |
} | |
} | |
private String getURL(HttpHeaders hh){ | |
List<String> persistenceXmlURLs = hh.getRequestHeader("persistenceXmlURL"); | |
if (persistenceXmlURLs == null || persistenceXmlURLs.isEmpty()) { | |
return null; | |
} | |
if (persistenceXmlURLs.size() != 1) { | |
throw new WebApplicationException(Status.BAD_REQUEST); | |
} | |
return persistenceXmlURLs.get(0); | |
} | |
@PreDestroy | |
public void close() { | |
factory.close(); | |
} | |
} |