| /* |
| * Copyright (c) 2011, 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: |
| // gonural - Initial implementation |
| package org.eclipse.persistence.jpa.rs.resources.common; |
| |
| import java.io.StringWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.net.URI; |
| import java.net.URLDecoder; |
| import java.net.URLEncoder; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.ServiceLoader; |
| import java.util.UUID; |
| |
| import jakarta.ws.rs.core.MediaType; |
| import jakarta.ws.rs.core.PathSegment; |
| import jakarta.ws.rs.core.UriInfo; |
| import jakarta.xml.bind.JAXBException; |
| import jakarta.xml.bind.Marshaller; |
| |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.Attribute; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.Descriptor; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.ItemLinks; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.Link; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.LinkTemplate; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.LinkV2; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.PersistenceUnit; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.Query; |
| import org.eclipse.persistence.internal.jpa.rs.metadata.model.v2.ContextsCatalog; |
| 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.jaxb.JAXBContext; |
| import org.eclipse.persistence.jaxb.JAXBContextFactory; |
| import org.eclipse.persistence.jaxb.MarshallerProperties; |
| import org.eclipse.persistence.jpa.rs.DataStorage; |
| import org.eclipse.persistence.jpa.rs.MatrixParameters; |
| import org.eclipse.persistence.jpa.rs.PersistenceContext; |
| import org.eclipse.persistence.jpa.rs.PersistenceContextFactory; |
| import org.eclipse.persistence.jpa.rs.PersistenceContextFactoryProvider; |
| import org.eclipse.persistence.jpa.rs.QueryParameters; |
| import org.eclipse.persistence.jpa.rs.exceptions.JPARSException; |
| import org.eclipse.persistence.jpa.rs.features.ServiceVersion; |
| import org.eclipse.persistence.jpa.rs.util.JPARSLogger; |
| import org.eclipse.persistence.jpa.rs.util.list.LinkList; |
| import org.eclipse.persistence.jpa.rs.util.list.QueryList; |
| |
| /** |
| * Base class for all resources. |
| * |
| * @author gonural |
| */ |
| public abstract class AbstractResource { |
| public static final String SERVICE_VERSION_FORMAT = "v\\d\\.\\d|latest"; |
| public static final String APPLICATION_SCHEMA_JSON ="application/schema+json"; |
| public static final MediaType APPLICATION_SCHEMA_JSON_TYPE = new MediaType("application","schema+json"); |
| protected PersistenceContextFactory factory; |
| |
| /** |
| * Sets the persistence factory. |
| * |
| * @param factory the new persistence factory |
| */ |
| public void setPersistenceFactory(PersistenceContextFactory factory) { |
| this.factory = factory; |
| } |
| |
| /** |
| * Gets the persistence factory. |
| * |
| * @return the persistence factory |
| */ |
| public PersistenceContextFactory getPersistenceFactory() { |
| return getPersistenceFactory(Thread.currentThread().getContextClassLoader()); |
| } |
| |
| /** |
| * Gets the persistence factory. |
| * |
| * @return the persistence factory |
| */ |
| public PersistenceContextFactory getPersistenceFactory(ClassLoader loader) { |
| if (factory == null) { |
| factory = buildPersistenceContextFactory(loader); |
| } |
| return factory; |
| } |
| |
| /** |
| * Builds the persistence context factory. |
| * |
| * @param loader the loader |
| * @return the persistence context factory |
| */ |
| protected PersistenceContextFactory buildPersistenceContextFactory(ClassLoader loader) { |
| ServiceLoader<PersistenceContextFactoryProvider> contextFactoryLoader = ServiceLoader.load(PersistenceContextFactoryProvider.class, loader); |
| |
| for (PersistenceContextFactoryProvider provider : contextFactoryLoader) { |
| PersistenceContextFactory factory = provider.getPersistenceContextFactory(null); |
| if (factory != null) { |
| return factory; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get a map of the matrix parameters associated with the URI path segment of the current request |
| * |
| * In JPA-RS, things that user sets (such as parameters of named queries, etc.) are treated as matrix parameters |
| * List of valid matrix parameters for JPA-RS is defined in MatrixParameters |
| * @see MatrixParameters |
| * |
| * @param info the info |
| * @param segment the segment |
| * @return the matrix parameters |
| */ |
| protected static Map<String, String> getMatrixParameters(UriInfo info, String segment) { |
| Map<String, String> matrixParameters = new HashMap<String, String>(); |
| for (PathSegment pathSegment : info.getPathSegments()) { |
| if (pathSegment.getPath() != null && pathSegment.getPath().equals(segment)) { |
| for (Entry<String, List<String>> entry : pathSegment.getMatrixParameters().entrySet()) { |
| matrixParameters.put(entry.getKey(), entry.getValue().get(0)); |
| } |
| return matrixParameters; |
| } |
| } |
| return matrixParameters; |
| } |
| |
| /** |
| * Get the URI query parameters of the current request |
| * |
| * In JPA-RS, predefined attributes (such as eclipselink query hints) are treated as query parameters |
| * List of valid query parameters for JPA-RS is defined in QueryParameters |
| * @see QueryParameters |
| * |
| * @param info the info |
| * @return the query parameters |
| */ |
| public static Map<String, Object> getQueryParameters(UriInfo info) { |
| Map<String, Object> queryParameters = new HashMap<String, Object>(); |
| for (String key : info.getQueryParameters().keySet()) { |
| queryParameters.put(key, info.getQueryParameters().getFirst(key)); |
| } |
| return queryParameters; |
| } |
| |
| /** |
| * Checks if is valid version. |
| * |
| * @param version the version |
| * @return true, if is valid version |
| */ |
| protected static boolean isValidVersion(String version) { |
| return ServiceVersion.hasCode(version); |
| } |
| |
| /** |
| * Gets the persistence context. |
| * |
| * @param persistenceUnit the persistence unit |
| * @param baseURI the base uri |
| * @param version the version |
| * @param initializationProperties the initialization properties |
| * @return the persistence context |
| */ |
| protected PersistenceContext getPersistenceContext(String persistenceUnit, String entityType, URI baseURI, String version, Map<String, Object> initializationProperties) { |
| if (!isValidVersion(version)) { |
| JPARSLogger.error("unsupported_service_version_in_the_request", new Object[] { version }); |
| throw new IllegalArgumentException(); |
| } |
| |
| PersistenceContext context = getPersistenceFactory().get(persistenceUnit, baseURI, version, initializationProperties); |
| if (context == null) { |
| JPARSLogger.error("jpars_could_not_find_persistence_context", new Object[] { persistenceUnit }); |
| throw JPARSException.persistenceContextCouldNotBeBootstrapped(persistenceUnit); |
| } |
| |
| if ((entityType != null) && (context.getClass(entityType) == null)) { |
| JPARSLogger.error(context.getSessionLog(), "jpars_could_not_find_class_in_persistence_unit", new Object[] { entityType, persistenceUnit }); |
| throw JPARSException.classOrClassDescriptorCouldNotBeFoundForEntity(entityType, persistenceUnit); |
| } |
| |
| return context; |
| } |
| |
| /** |
| * Gets the relationship partner. |
| * |
| * @param matrixParams the matrix params |
| * @param queryParams the query params |
| * @return the relationship partner |
| */ |
| protected String getRelationshipPartner(Map<String, String> matrixParams, Map<String, Object> queryParams) { |
| String partner = null; |
| // Fix for Bug 396791 - JPA-RS: partner should be treated as a query parameter |
| // For backwards compatibility, we check both, matrix and query parameters. |
| if ((queryParams != null) && (!queryParams.isEmpty())) { |
| partner = (String) queryParams.get(QueryParameters.JPARS_RELATIONSHIP_PARTNER); |
| } |
| |
| if (partner == null) { |
| if ((matrixParams != null) && (!matrixParams.isEmpty())) { |
| partner = matrixParams.get(MatrixParameters.JPARS_RELATIONSHIP_PARTNER); |
| } |
| } |
| return partner; |
| } |
| |
| /** |
| * Marshall metadata. |
| * |
| * @param metadata the metadata |
| * @param mediaType the media type |
| * @return the string |
| * @throws JAXBException the jAXB exception |
| */ |
| protected String marshallMetadata(Object metadata, String mediaType) throws JAXBException { |
| final Class<?>[] jaxbClasses = new Class[] { Link.class, Attribute.class, Descriptor.class, LinkTemplate.class, PersistenceUnit.class, Query.class, LinkList.class, QueryList.class, |
| ResourceSchema.class, Property.class, Reference.class, LinkV2.class, MetadataCatalog.class, Resource.class, ItemLinks.class, ContextsCatalog.class }; |
| final JAXBContext context = (JAXBContext) JAXBContextFactory.createContext(jaxbClasses, null); |
| final Marshaller marshaller = context.createMarshaller(); |
| marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, Boolean.FALSE); |
| marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, mediaType); |
| marshaller.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true); |
| |
| final StringWriter writer = new StringWriter(); |
| marshaller.marshal(metadata, writer); |
| return writer.toString(); |
| } |
| |
| @SuppressWarnings("unused") |
| private static String getEncodedUri(String uri) { |
| try { |
| return URLEncoder.encode(uri, "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| // TODO: Log it |
| return uri; |
| } |
| } |
| |
| @SuppressWarnings("unused") |
| private static String getDecodedUri(String uri) { |
| try { |
| return URLDecoder.decode(uri, "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| // TODO: Log it |
| return uri; |
| } |
| } |
| |
| protected void setRequestUniqueId() { |
| DataStorage.set(DataStorage.REQUEST_ID, UUID.randomUUID().toString()); |
| } |
| } |