org.eclipse.persistence.jpa.rs.resources.MetadataResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2014, 2020 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Dmitry Kornilov - Initial implementation
package org.eclipse.persistence.jpa.rs.resources;
import static org.eclipse.persistence.jpa.rs.resources.common.AbstractResource.SERVICE_VERSION_FORMAT;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import jakarta.xml.bind.JAXBException;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.eis.mappings.EISCompositeCollectionMapping;
import org.eclipse.persistence.internal.expressions.ConstantExpression;
import org.eclipse.persistence.internal.expressions.MapEntryExpression;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.LinkV2;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.MetadataCatalog;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.Property;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.Reference;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.Resource;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.ResourceSchema;
import org.eclipse.persistence.internal.queries.ReportItem;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.rs.PersistenceContext;
import org.eclipse.persistence.jpa.rs.exceptions.JPARSException;
import org.eclipse.persistence.jpa.rs.features.ItemLinksBuilder;
import org.eclipse.persistence.jpa.rs.resources.common.AbstractResource;
import org.eclipse.persistence.jpa.rs.util.HrefHelper;
import org.eclipse.persistence.jpa.rs.util.JPARSLogger;
import org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller;
import org.eclipse.persistence.mappings.CollectionMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
/**
* JPARS 2.0 metadata catalog. Resource metadata and schemas.
*
* @author Dmitry Kornilov
* @since EclipseLink 2.6.0.
*/
@Produces({ MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_JSON })
@Path("/{version : " + SERVICE_VERSION_FORMAT + "}/{context}/metadata-catalog/")
public class MetadataResource extends AbstractResource {
private static final String CLASS_NAME = MetadataResource.class.getName();
private static final Map, String> PRIMITIVE_TO_JSON = new HashMap, String>();
static {
PRIMITIVE_TO_JSON.put(boolean.class, "boolean");
PRIMITIVE_TO_JSON.put(byte.class, "number");
PRIMITIVE_TO_JSON.put(char.class, "string");
PRIMITIVE_TO_JSON.put(double.class, "number");
PRIMITIVE_TO_JSON.put(float.class, "number");
PRIMITIVE_TO_JSON.put(int.class, "integer");
PRIMITIVE_TO_JSON.put(long.class, "integer");
PRIMITIVE_TO_JSON.put(short.class, "number");
PRIMITIVE_TO_JSON.put(short.class, "number");
}
/**
* Returns metadata catalog.
*/
@GET
public Response getMetadataCatalog(@PathParam("version") String version,
@PathParam("context") String persistenceUnit,
@Context HttpHeaders httpHeaders,
@Context UriInfo uriInfo) {
setRequestUniqueId();
return buildMetadataCatalogResponse(version, persistenceUnit, httpHeaders, uriInfo);
}
/**
* Returns entity metadata if accepted media type is 'application/json' or entity schema if
* accepted media type is 'application/schema+json'.
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, AbstractResource.APPLICATION_SCHEMA_JSON })
@Consumes({ MediaType.APPLICATION_JSON, AbstractResource.APPLICATION_SCHEMA_JSON })
@Path("entity/{entityName}")
public Response getEntityResource(@PathParam("version") String version,
@PathParam("context") String persistenceUnit,
@PathParam("entityName") String entityName,
@Context HttpHeaders httpHeaders,
@Context UriInfo uriInfo) {
setRequestUniqueId();
final MediaType mediaType = StreamingOutputMarshaller.mediaType(httpHeaders.getAcceptableMediaTypes());
// Return schema if application.schema+json media type is requested otherwise return entity metadata
if (mediaType.equals(AbstractResource.APPLICATION_SCHEMA_JSON_TYPE)) {
return buildEntitySchemaResponse(version, persistenceUnit, entityName, uriInfo);
} else {
return buildEntityMetadataResponse(version, persistenceUnit, entityName, httpHeaders, uriInfo);
}
}
/**
* Returns query metadata if accepted media type is 'application/json' or entity schema if
* accepted media type is 'application/schema+json'.
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, AbstractResource.APPLICATION_SCHEMA_JSON })
@Consumes({ MediaType.APPLICATION_JSON, AbstractResource.APPLICATION_SCHEMA_JSON })
@Path("query/{queryName}")
public Response getQueryResource(@PathParam("version") String version,
@PathParam("context") String persistenceUnit,
@PathParam("queryName") String queryName,
@Context HttpHeaders httpHeaders,
@Context UriInfo uriInfo) {
setRequestUniqueId();
final MediaType mediaType = StreamingOutputMarshaller.mediaType(httpHeaders.getAcceptableMediaTypes());
// Return schema if application.schema+json media type is requested otherwise return entity metadata
if (mediaType.equals(AbstractResource.APPLICATION_SCHEMA_JSON_TYPE)) {
return buildQuerySchemaResponse(version, persistenceUnit, queryName, uriInfo);
} else {
return buildQueryMetadataResponse(version, persistenceUnit, queryName, httpHeaders, uriInfo);
}
}
private Response buildMetadataCatalogResponse(String version, String persistenceUnit, HttpHeaders httpHeaders, UriInfo uriInfo) {
JPARSLogger.entering(CLASS_NAME, "buildMetadataCatalogResponse", new Object[]{"GET", version, persistenceUnit, uriInfo.getRequestUri().toASCIIString()});
final String result;
try {
final PersistenceContext context = getPersistenceContext(persistenceUnit, null, uriInfo.getBaseUri(), version, null);
final MetadataCatalog catalog = buildMetadataCatalog(context);
final String mediaType = StreamingOutputMarshaller.mediaType(httpHeaders.getAcceptableMediaTypes()).toString();
result = marshallMetadata(catalog, mediaType);
} catch (JAXBException e) {
throw JPARSException.exceptionOccurred(e);
}
return Response.ok(new StreamingOutputMarshaller(null, result, httpHeaders.getAcceptableMediaTypes())).build();
}
private Response buildEntityMetadataResponse(String version, String persistenceUnit, String entityName, HttpHeaders httpHeaders, UriInfo uriInfo) {
JPARSLogger.entering(CLASS_NAME, "buildEntityMetadataResponse", new Object[]{"GET", version, persistenceUnit, entityName, uriInfo.getRequestUri().toASCIIString()});
final String result;
try {
final PersistenceContext context = getPersistenceContext(persistenceUnit, null, uriInfo.getBaseUri(), version, null);
final ClassDescriptor descriptor = context.getServerSession().getDescriptorForAlias(entityName);
if (descriptor == null) {
JPARSLogger.error(context.getSessionLog(), "jpars_could_not_find_entity_type", new Object[] { entityName, persistenceUnit });
throw JPARSException.classOrClassDescriptorCouldNotBeFoundForEntity(entityName, persistenceUnit);
} else {
final String mediaType = StreamingOutputMarshaller.mediaType(httpHeaders.getAcceptableMediaTypes()).toString();
final Resource resource = buildEntityMetadata(context, descriptor);
result = marshallMetadata(resource, mediaType);
}
} catch (JAXBException e) {
throw JPARSException.exceptionOccurred(e);
}
return Response.ok(new StreamingOutputMarshaller(null, result, httpHeaders.getAcceptableMediaTypes())).build();
}
private Response buildQueryMetadataResponse(String version, String persistenceUnit, String queryName, HttpHeaders httpHeaders, UriInfo uriInfo) {
JPARSLogger.entering(CLASS_NAME, "buildQueryMetadataResponse", new Object[]{"GET", version, persistenceUnit, queryName, uriInfo.getRequestUri().toASCIIString()});
final String result;
try {
final PersistenceContext context = getPersistenceContext(persistenceUnit, null, uriInfo.getBaseUri(), version, null);
// We need to make sure that query with given name exists
final DatabaseQuery query = context.getServerSession().getQuery(queryName);
if (query == null) {
JPARSLogger.error(context.getSessionLog(), "jpars_could_not_find_query", new Object[] {queryName, persistenceUnit});
throw JPARSException.responseCouldNotBeBuiltForNamedQueryRequest(queryName, context.getName());
}
final String mediaType = StreamingOutputMarshaller.mediaType(httpHeaders.getAcceptableMediaTypes()).toString();
final Resource resource = buildQueryMetadata(context, query);
result = marshallMetadata(resource, mediaType);
} catch (JAXBException e) {
throw JPARSException.exceptionOccurred(e);
}
return Response.ok(new StreamingOutputMarshaller(null, result, httpHeaders.getAcceptableMediaTypes())).build();
}
private Response buildEntitySchemaResponse(String version, String persistenceUnit, String entityName, UriInfo uriInfo) {
JPARSLogger.entering(CLASS_NAME, "buildEntitySchemaResponse", new Object[]{"GET", version, persistenceUnit, uriInfo.getRequestUri().toASCIIString()});
final String result;
try {
final PersistenceContext context = getPersistenceContext(persistenceUnit, null, uriInfo.getBaseUri(), version, null);
final ClassDescriptor descriptor = context.getServerSession().getDescriptorForAlias(entityName);
if (descriptor == null) {
JPARSLogger.error(context.getSessionLog(), "jpars_could_not_find_entity_type", new Object[] { entityName, persistenceUnit });
throw JPARSException.classOrClassDescriptorCouldNotBeFoundForEntity(entityName, persistenceUnit);
} else {
final ResourceSchema schema = new ResourceSchema();
schema.setTitle(descriptor.getAlias());
schema.setSchema(HrefHelper.buildEntityMetadataHref(context, descriptor.getAlias()) + "#");
schema.addAllOf(new Reference(HrefHelper.buildBaseRestSchemaRef("#/singularResource")));
// Properties
for (DatabaseMapping databaseMapping : descriptor.getMappings()) {
schema.addProperty(databaseMapping.getAttributeName(), buildProperty(context, databaseMapping));
}
// Links
final String instancesHref = HrefHelper.buildEntityDescribesHref(context, descriptor.getAlias());
schema.setLinks((new ItemLinksBuilder())
.addDescribedBy(HrefHelper.buildEntityMetadataHref(context, descriptor.getAlias()))
.addFind(instancesHref + "/{primaryKey}")
.addCreate(instancesHref)
.addUpdate(instancesHref)
.addDelete(instancesHref + "/{primaryKey}")
.getList());
result = marshallMetadata(schema, MediaType.APPLICATION_JSON);
}
} catch (JAXBException e) {
throw JPARSException.exceptionOccurred(e);
}
return Response.ok(new StreamingOutputMarshaller(null, result, AbstractResource.APPLICATION_SCHEMA_JSON_TYPE)).build();
}
private Response buildQuerySchemaResponse(String version, String persistenceUnit, String queryName, UriInfo uriInfo) {
JPARSLogger.entering(CLASS_NAME, "buildQuerySchemaResponse", new Object[]{"GET", version, persistenceUnit, uriInfo.getRequestUri().toASCIIString()});
final String result;
try {
final PersistenceContext context = getPersistenceContext(persistenceUnit, null, uriInfo.getBaseUri(), version, null);
// We need to make sure that query with given name exists
final DatabaseQuery query = context.getServerSession().getQuery(queryName);
if (query == null) {
JPARSLogger.error(context.getSessionLog(), "jpars_could_not_find_query", new Object[] {queryName, persistenceUnit});
throw JPARSException.responseCouldNotBeBuiltForNamedQueryRequest(queryName, context.getName());
}
final ResourceSchema querySchema = buildQuerySchema(context, query);
result = marshallMetadata(querySchema, MediaType.APPLICATION_JSON);
} catch (JAXBException e) {
throw JPARSException.exceptionOccurred(e);
}
return Response.ok(new StreamingOutputMarshaller(null, result, AbstractResource.APPLICATION_SCHEMA_JSON_TYPE)).build();
}
private MetadataCatalog buildMetadataCatalog(PersistenceContext context) {
final MetadataCatalog result = new MetadataCatalog();
// Entities
final Map descriptors = context.getServerSession().getDescriptors();
for (ClassDescriptor descriptor : descriptors.values()) {
// Skip embeddables
if (!descriptor.isAggregateDescriptor()) {
result.addResource(buildEntityMetadata(context, descriptor));
}
}
// Queries
final Map> allQueries = context.getServerSession().getQueries();
for (List databaseQueries : allQueries.values()) {
if (databaseQueries != null) {
for (DatabaseQuery query : databaseQueries) {
if (query.getReferenceClassName() != null) {
result.addResource(buildQueryMetadata(context, query));
}
}
}
}
final String href = HrefHelper.buildMetadataCatalogHref(context);
final List links = (new ItemLinksBuilder())
.addCanonical(href)
.getList();
result.setLinks(links);
return result;
}
private Resource buildEntityMetadata(PersistenceContext context, ClassDescriptor descriptor) {
final Resource resource = new Resource();
resource.setName(descriptor.getAlias());
final String metadataHref = HrefHelper.buildEntityMetadataHref(context, descriptor.getAlias());
final List links = (new ItemLinksBuilder())
.addAlternate(metadataHref)
.addCanonical(metadataHref, MediaType.APPLICATION_JSON)
.addDescribes(HrefHelper.buildEntityDescribesHref(context, descriptor.getAlias()))
.getList();
resource.setLinks(links);
return resource;
}
private Resource buildQueryMetadata(PersistenceContext context, DatabaseQuery query) {
final Resource resource = new Resource();
resource.setName(query.getName());
final String metadataHref = HrefHelper.buildQueryMetadataHref(context, query.getName());
final List links = (new ItemLinksBuilder())
.addAlternate(metadataHref)
.addCanonical(metadataHref, MediaType.APPLICATION_JSON)
.addDescribes(HrefHelper.buildQueryDescribesHref(context, query.getName()))
.getList();
resource.setLinks(links);
return resource;
}
private ResourceSchema buildQuerySchema(PersistenceContext context, DatabaseQuery query) {
final ResourceSchema schema = new ResourceSchema();
schema.setTitle(query.getName());
schema.setSchema(HrefHelper.buildQueryMetadataHref(context, query.getName()) + "#");
schema.addAllOf(new Reference(HrefHelper.buildBaseRestSchemaRef("#/collectionBaseResource")));
// Link
final String method = query.isReadQuery() ? "GET" : "POST";
schema.setLinks((new ItemLinksBuilder())
.addExecute(HrefHelper.buildQueryHref(context, query.getName(), getQueryParamString(query)), method)
.getList());
// Definitions
if (query.isReportQuery()) {
// In case of report query we need to define a returned type
final ResourceSchema returnType = new ResourceSchema();
query.checkPrepare((AbstractSession) context.getServerSession(), new DatabaseRecord());
for (ReportItem item : ((ReportQuery) query).getItems()) {
final Property property;
if (item.getMapping() != null) {
if (item.getAttributeExpression() != null && item.getAttributeExpression().isMapEntryExpression()) {
if (((MapEntryExpression)item.getAttributeExpression()).shouldReturnMapEntry()) {
property = buildProperty(context, Map.Entry.class);
} else {
property = buildProperty(context, ((Class>) item.getMapping().getContainerPolicy().getKeyType()));
}
} else {
property = buildProperty(context, item.getMapping().getAttributeClassification());
}
} else if (item.getResultType() != null) {
property = buildProperty(context, item.getResultType());
} else if (item.getDescriptor() != null) {
property = buildProperty(context, item.getDescriptor().getJavaClass());
} else if (item.getAttributeExpression() != null && item.getAttributeExpression().isConstantExpression()) {
property = buildProperty(context, ((ConstantExpression) item.getAttributeExpression()).getValue().getClass());
} else {
// Use Object.class by default.
property = buildProperty(context, Object.class);
}
returnType.addProperty(item.getName(), property);
}
schema.addDefinition("result", returnType);
final Property items = new Property();
items.setType("array");
items.setItems(new Property("#/definitions/result"));
schema.addProperty("items", items);
} else {
// Read all query. Each item is an entity. Make a JSON pointer.
if (query.getReferenceClassName() != null) {
final Property items = new Property();
items.setType("array");
items.setItems(new Property(HrefHelper.buildEntityMetadataHref(context, query.getReferenceClass().getSimpleName()) + "#"));
schema.addProperty("items", items);
}
}
return schema;
}
private String getQueryParamString(DatabaseQuery query) {
final StringBuilder queryParams = new StringBuilder();
for (String arg : query.getArguments()) {
queryParams.append(";");
queryParams.append(arg).append("={").append(arg).append("}");
}
return queryParams.toString();
}
private Property buildProperty(PersistenceContext context, DatabaseMapping mapping) {
if (mapping.isCollectionMapping()) {
final Property property = new Property();
property.setType("array");
property.setItems(buildProperty(context, getCollectionGenericClass(mapping)));
return property;
} else if (mapping.isForeignReferenceMapping()) {
final ForeignReferenceMapping foreignReferenceMapping = (ForeignReferenceMapping)mapping;
final String href = HrefHelper.buildEntityMetadataHref(context, foreignReferenceMapping.getReferenceClass().getSimpleName() + "#");
return new Property(href);
} else {
return buildProperty(context, mapping.getAttributeClassification());
}
}
private Property buildProperty(PersistenceContext context, Class> clazz) {
final Property property = new Property();
if (context.getServerSession().getDescriptorForAlias(clazz.getSimpleName()) != null) {
property.setRef(HrefHelper.buildEntityMetadataHref(context, clazz.getSimpleName()) + "#");
} else if (Number.class.isAssignableFrom(clazz)) {
property.setType("number");
} else if (Boolean.class.equals(clazz)) {
property.setType("boolean");
} else if (String.class.equals(clazz)) {
property.setType("string");
} else if (Collection.class.isAssignableFrom(clazz)) {
property.setType("array");
} else if (clazz.isPrimitive()) {
property.setType(PRIMITIVE_TO_JSON.get(clazz));
} else {
property.setType("object");
}
return property;
}
private Class> getCollectionGenericClass(DatabaseMapping mapping) {
Class> collectionName = null;
if (mapping.isEISMapping()) {
final EISCompositeCollectionMapping collectionMapping = (EISCompositeCollectionMapping) mapping;
if (collectionMapping.getReferenceClass() != null) {
collectionName = collectionMapping.getReferenceClass();
}
if ((collectionName == null) && (collectionMapping.getAttributeClassification() != null)) {
collectionName = collectionMapping.getAttributeClassification();
}
} else {
final CollectionMapping collectionMapping = (CollectionMapping) mapping;
if (collectionMapping.getReferenceClass() != null) {
collectionName = collectionMapping.getReferenceClass();
}
if ((collectionName == null) && (collectionMapping.getAttributeClassification() != null)) {
collectionName = collectionMapping.getAttributeClassification();
}
}
return collectionName;
}
}