| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| ******************************************************************************/ |
| package org.apache.olingo.odata2.jpa.processor.core.access.data; |
| |
| import java.io.InputStream; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| import javax.persistence.EntityManager; |
| |
| import org.apache.olingo.odata2.api.edm.EdmEntitySet; |
| import org.apache.olingo.odata2.api.edm.EdmException; |
| import org.apache.olingo.odata2.api.edm.EdmNavigationProperty; |
| import org.apache.olingo.odata2.api.edm.EdmSimpleType; |
| import org.apache.olingo.odata2.api.ep.entry.ODataEntry; |
| import org.apache.olingo.odata2.api.exception.ODataException; |
| import org.apache.olingo.odata2.api.uri.KeyPredicate; |
| import org.apache.olingo.odata2.api.uri.NavigationSegment; |
| import org.apache.olingo.odata2.api.uri.UriInfo; |
| import org.apache.olingo.odata2.api.uri.info.DeleteUriInfo; |
| import org.apache.olingo.odata2.api.uri.info.GetEntitySetUriInfo; |
| import org.apache.olingo.odata2.api.uri.info.GetEntityUriInfo; |
| import org.apache.olingo.odata2.api.uri.info.PostUriInfo; |
| import org.apache.olingo.odata2.api.uri.info.PutMergePatchUriInfo; |
| import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext; |
| import org.apache.olingo.odata2.jpa.processor.api.ODataJPATransaction; |
| import org.apache.olingo.odata2.jpa.processor.api.access.JPAProcessor; |
| import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException; |
| import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException; |
| import org.apache.olingo.odata2.jpa.processor.api.factory.ODataJPAFactory; |
| import org.apache.olingo.odata2.jpa.processor.core.ODataEntityParser; |
| |
| public class JPALink { |
| |
| private static final String SPACE = " "; |
| private static final String ODATA_COMMAND_FILTER = "$filter"; |
| private static final String ODATA_OPERATOR_OR = "or"; |
| private static final String ODATA_OPERATOR_NE = "ne"; |
| |
| private ODataJPAContext context; |
| private JPAProcessor jpaProcessor; |
| private ODataEntityParser parser; |
| private Object targetJPAEntity; |
| private Object sourceJPAEntity; |
| |
| public JPALink(final ODataJPAContext context) { |
| this.context = context; |
| jpaProcessor = ODataJPAFactory.createFactory().getJPAAccessFactory().getJPAProcessor(this.context); |
| parser = new ODataEntityParser(this.context); |
| } |
| |
| public void setSourceJPAEntity(final Object jpaEntity) { |
| sourceJPAEntity = jpaEntity; |
| } |
| |
| public void setTargetJPAEntity(final Object jpaEntity) { |
| targetJPAEntity = jpaEntity; |
| } |
| |
| public Object getTargetJPAEntity() { |
| return targetJPAEntity; |
| } |
| |
| public Object getSourceJPAEntity() { |
| return sourceJPAEntity; |
| } |
| |
| public void create(final PostUriInfo uriInfo, final InputStream content, final String requestContentType, |
| final String contentType) throws ODataJPARuntimeException, ODataJPAModelException { |
| modifyLink((UriInfo) uriInfo, content, requestContentType, contentType); |
| } |
| |
| public void update(final PutMergePatchUriInfo putUriInfo, final InputStream content, final String requestContentType, |
| final String contentType) throws ODataJPARuntimeException, ODataJPAModelException { |
| modifyLink((UriInfo) putUriInfo, content, requestContentType, contentType); |
| } |
| |
| public void delete(final DeleteUriInfo uriInfo) throws ODataJPARuntimeException { |
| try { |
| |
| int index = context.getODataContext().getPathInfo().getODataSegments().size() - 2; |
| |
| List<String> linkSegments = new ArrayList<String>(); |
| String customLinkSegment = context.getODataContext().getPathInfo().getODataSegments().get(0).getPath(); |
| linkSegments.add(customLinkSegment); |
| customLinkSegment = uriInfo.getNavigationSegments().get(0).getNavigationProperty().getName(); |
| linkSegments.add(customLinkSegment); |
| |
| HashMap<String, String> options = new HashMap<String, String>(); |
| List<KeyPredicate> keyPredicates = uriInfo.getNavigationSegments().get(0).getKeyPredicates(); |
| StringBuffer condition = new StringBuffer(); |
| String literal = null; |
| KeyPredicate keyPredicate = null; |
| int size = keyPredicates.size(); |
| for (int i = 0; i < size; i++) { |
| keyPredicate = keyPredicates.get(i); |
| |
| literal = ((EdmSimpleType) keyPredicate.getProperty().getType()).toUriLiteral(keyPredicate.getLiteral()); |
| condition.append(keyPredicate.getProperty().getName()).append(SPACE); |
| condition.append(ODATA_OPERATOR_NE).append(SPACE); |
| condition.append(literal).append(SPACE); |
| if (i != size - 1) { |
| condition.append(ODATA_OPERATOR_OR).append(SPACE); |
| } |
| } |
| if (condition.length() > 0) { |
| options.put(ODATA_COMMAND_FILTER, condition.toString()); |
| } |
| |
| UriInfo parsedUriInfo = parser.parseLinkSegments(linkSegments, options); |
| List<Object> relatedEntities = jpaProcessor.process((GetEntitySetUriInfo) parsedUriInfo); |
| |
| parsedUriInfo = parser.parseURISegment(0, index); |
| if (parsedUriInfo != null) { |
| targetJPAEntity = jpaProcessor.process((GetEntityUriInfo) parsedUriInfo); |
| if (targetJPAEntity == null) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.RESOURCE_X_NOT_FOUND |
| .addContent(parsedUriInfo.getTargetEntitySet().getName()), null); |
| } |
| NavigationSegment navigationSegment = uriInfo.getNavigationSegments().get(0); |
| EdmNavigationProperty navigationProperty = navigationSegment.getNavigationProperty(); |
| delinkJPAEntities(targetJPAEntity, relatedEntities, navigationProperty); |
| } |
| |
| } catch (EdmException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.GENERAL.addContent(e.getMessage()), e); |
| } catch (ODataException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.GENERAL.addContent(e.getMessage()), e); |
| } |
| } |
| |
| public void save() { |
| EntityManager em = context.getEntityManager(); |
| ODataJPATransaction tx = context.getODataJPATransaction(); |
| boolean isLocalTransaction = false; |
| if (!tx.isActive()) { |
| tx.begin(); |
| isLocalTransaction = true; |
| } |
| if (sourceJPAEntity != null) { |
| em.persist(sourceJPAEntity); |
| } |
| if (targetJPAEntity != null) { |
| em.persist(targetJPAEntity); |
| } |
| if (isLocalTransaction |
| && ((sourceJPAEntity != null && em.contains(sourceJPAEntity)) || (targetJPAEntity != null && em |
| .contains(targetJPAEntity)))) { |
| tx.commit(); |
| } |
| } |
| |
| public void create(final EdmEntitySet entitySet, final ODataEntry oDataEntry, |
| final List<String> navigationPropertyNames) |
| throws ODataJPARuntimeException, |
| ODataJPAModelException { |
| |
| List<Object> targetJPAEntities = new ArrayList<Object>(); |
| try { |
| for (String navPropertyName : navigationPropertyNames) { |
| List<String> links = oDataEntry.getMetadata().getAssociationUris(navPropertyName); |
| if (links == null || links.isEmpty() == true) { |
| links = extractLinkURI(oDataEntry, navPropertyName); |
| } |
| if (links != null && links.isEmpty() == false) { |
| |
| EdmNavigationProperty navProperty = (EdmNavigationProperty) entitySet.getEntityType() |
| .getProperty( |
| navPropertyName); |
| |
| for (String link : links) { |
| UriInfo bindingUriInfo = parser.parseBindingLink(link, new HashMap<String, String>()); |
| targetJPAEntity = jpaProcessor.process((GetEntityUriInfo) bindingUriInfo); |
| if (targetJPAEntity != null) { |
| targetJPAEntities.add(targetJPAEntity); |
| } |
| } |
| if (!targetJPAEntities.isEmpty()) { |
| linkJPAEntities(targetJPAEntities, sourceJPAEntity, navProperty); |
| } |
| targetJPAEntities.clear(); |
| } |
| } |
| } catch (EdmException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.GENERAL.addContent(e.getMessage()), e); |
| } |
| |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static void linkJPAEntities(final Collection<Object> targetJPAEntities, final Object sourceJPAEntity, |
| final EdmNavigationProperty navigationProperty) throws ODataJPARuntimeException { |
| if (targetJPAEntities == null || sourceJPAEntity == null || navigationProperty == null) { |
| return; |
| } |
| try { |
| JPAEntityParser entityParser = new JPAEntityParser(); |
| Method setMethod = entityParser.getAccessModifier(sourceJPAEntity.getClass(), |
| navigationProperty, JPAEntityParser.ACCESS_MODIFIER_SET); |
| switch (navigationProperty.getMultiplicity()) { |
| case MANY: |
| Method getMethod = entityParser.getAccessModifier(sourceJPAEntity.getClass(), |
| navigationProperty, JPAEntityParser.ACCESS_MODIFIER_GET); |
| Collection<Object> relatedEntities = (Collection<Object>) getMethod.invoke(sourceJPAEntity); |
| if (relatedEntities == null) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.ERROR_JPQL_CREATE_REQUEST, null); |
| } |
| relatedEntities.addAll(targetJPAEntities); |
| setMethod.invoke(sourceJPAEntity, relatedEntities); |
| break; |
| case ONE: |
| case ZERO_TO_ONE: |
| setMethod.invoke(sourceJPAEntity, targetJPAEntities.iterator().next()); |
| break; |
| } |
| } catch (EdmException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (IllegalArgumentException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (IllegalAccessException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (InvocationTargetException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private List<String> extractLinkURI(ODataEntry oDataEntry, String navigationPropertyName) { |
| |
| List<String> links = new ArrayList<String>(); |
| |
| String link = null; |
| Object object = oDataEntry.getProperties().get(navigationPropertyName); |
| if (object == null) { |
| return links; |
| } |
| if (object instanceof ODataEntry) { |
| link = ((ODataEntry) object).getMetadata().getUri(); |
| if (!link.isEmpty()) { |
| links.add(link); |
| } |
| } else { |
| for (ODataEntry entry : (List<ODataEntry>) object) { |
| link = entry.getMetadata().getUri(); |
| if (link != null && link.isEmpty() == false) { |
| links.add(link); |
| } |
| } |
| } |
| |
| return links; |
| } |
| |
| private void modifyLink(final UriInfo uriInfo, final InputStream content, final String requestContentType, |
| final String contentType) |
| throws ODataJPARuntimeException, ODataJPAModelException { |
| try { |
| EdmEntitySet targetEntitySet = uriInfo.getTargetEntitySet(); |
| String targerEntitySetName = targetEntitySet.getName(); |
| EdmNavigationProperty navigationProperty = null; |
| UriInfo getUriInfo = null; |
| |
| if (uriInfo.isLinks()) { |
| getUriInfo = parser.parseLink(targetEntitySet, content, requestContentType); |
| navigationProperty = uriInfo.getNavigationSegments().get(0).getNavigationProperty(); |
| } else { |
| return; |
| } |
| |
| if (!getUriInfo.getTargetEntitySet().getName().equals(targerEntitySetName)) |
| { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.RELATIONSHIP_INVALID, null); |
| } |
| |
| targetJPAEntity = jpaProcessor.process((GetEntityUriInfo) getUriInfo); |
| if (targetJPAEntity != null && sourceJPAEntity == null) { |
| int index = context.getODataContext().getPathInfo().getODataSegments().size() - 2; |
| getUriInfo = parser.parseURISegment(0, index); |
| sourceJPAEntity = jpaProcessor.process((GetEntityUriInfo) getUriInfo); |
| if (sourceJPAEntity == null) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.RESOURCE_X_NOT_FOUND |
| .addContent(getUriInfo.getTargetEntitySet().getName()), null); |
| } |
| } |
| if (targetJPAEntity != null && sourceJPAEntity != null) { |
| List<Object> targetJPAEntities = new ArrayList<Object>(); |
| targetJPAEntities.add(targetJPAEntity); |
| linkJPAEntities(targetJPAEntities, sourceJPAEntity, navigationProperty); |
| } |
| |
| } catch (IllegalArgumentException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (EdmException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (ODataException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.GENERAL.addContent(e.getMessage()), e); |
| } |
| } |
| |
| private void delinkJPAEntities(final Object jpaEntity, |
| final List<Object> relatedJPAEntities, |
| final EdmNavigationProperty targetNavigationProperty) |
| throws ODataJPARuntimeException { |
| |
| try { |
| JPAEntityParser entityParser = new JPAEntityParser(); |
| Method setMethod = entityParser.getAccessModifier(jpaEntity.getClass(), |
| targetNavigationProperty, JPAEntityParser.ACCESS_MODIFIER_SET); |
| |
| Method getMethod = entityParser.getAccessModifier(jpaEntity.getClass(), |
| targetNavigationProperty, JPAEntityParser.ACCESS_MODIFIER_GET); |
| |
| if (getMethod.getReturnType().getTypeParameters() != null |
| && getMethod.getReturnType().getTypeParameters().length != 0) { |
| setMethod.invoke(jpaEntity, relatedJPAEntities); |
| } else { |
| setMethod.invoke(jpaEntity, (Object) null); |
| } |
| } catch (IllegalAccessException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } catch (InvocationTargetException e) { |
| throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.INNER_EXCEPTION, e); |
| } |
| |
| } |
| } |