/******************************************************************************* * 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.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.olingo.odata2.api.edm.EdmEntitySet; import org.apache.olingo.odata2.api.edm.EdmEntityType; import org.apache.olingo.odata2.api.edm.EdmException; import org.apache.olingo.odata2.api.edm.EdmNavigationProperty; import org.apache.olingo.odata2.api.edm.EdmProperty; import org.apache.olingo.odata2.api.edm.EdmSimpleType; import org.apache.olingo.odata2.api.edm.EdmStructuralType; import org.apache.olingo.odata2.api.edm.EdmTypeKind; import org.apache.olingo.odata2.api.edm.EdmTyped; import org.apache.olingo.odata2.api.edm.EdmType; import org.apache.olingo.odata2.api.ep.entry.EntryMetadata; import org.apache.olingo.odata2.api.ep.entry.ODataEntry; import org.apache.olingo.odata2.api.ep.feed.ODataFeed; import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext; import org.apache.olingo.odata2.jpa.processor.api.OnJPAWriteContent; 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.model.JPAEdmMapping; import org.apache.olingo.odata2.jpa.processor.core.model.JPAEdmMappingImpl; public class JPAEntity { private Object jpaEntity = null; private JPAEntity parentJPAEntity = null; private EdmEntityType oDataEntityType = null; private EdmEntitySet oDataEntitySet = null; private Class<?> jpaType = null; private HashMap<String, Method> accessModifiersWrite = null; private JPAEntityParser jpaEntityParser = null; private ODataJPAContext oDataJPAContext; private OnJPAWriteContent onJPAWriteContent = null; private List<String> relatedJPAEntityLink = new ArrayList<String>(); public HashMap<String, List<Object>> relatedJPAEntityMap = null; private EdmNavigationProperty viaNavigationProperty; public JPAEntity(final EdmEntityType oDataEntityType, final EdmEntitySet oDataEntitySet, final ODataJPAContext context) { this.oDataEntityType = oDataEntityType; this.oDataEntitySet = oDataEntitySet; oDataJPAContext = context; try { JPAEdmMapping mapping = (JPAEdmMapping) oDataEntityType.getMapping(); jpaType = mapping.getJPAType(); } catch (EdmException e) { return; } jpaEntityParser = new JPAEntityParser(); onJPAWriteContent = oDataJPAContext.getODataContext().getServiceFactory().getCallback(OnJPAWriteContent.class); } public void setAccessModifersWrite(final HashMap<String, Method> accessModifiersWrite) { this.accessModifiersWrite = accessModifiersWrite; } public void setParentJPAEntity(final JPAEntity jpaEntity) { parentJPAEntity = jpaEntity; } public JPAEntity getParentJPAEntity() { return parentJPAEntity; } public Object getJPAEntity() { return jpaEntity; } public void setViaNavigationProperty(EdmNavigationProperty viaNavigationProperty) { this.viaNavigationProperty = viaNavigationProperty; } public EdmNavigationProperty getViaNavigationProperty() { return viaNavigationProperty; } public void create(final ODataEntry oDataEntry) throws ODataJPARuntimeException { if (oDataEntry == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL, null); } try { EntryMetadata entryMetadata = oDataEntry.getMetadata(); Map<String, Object> oDataEntryProperties = oDataEntry.getProperties(); if (oDataEntry.containsInlineEntry()) { normalizeInlineEntries(oDataEntryProperties); } if (oDataEntry.getProperties().size() > 0) { write(oDataEntryProperties, true); for (String navigationPropertyName : oDataEntityType.getNavigationPropertyNames()) { EdmNavigationProperty navProperty = (EdmNavigationProperty) oDataEntityType.getProperty(navigationPropertyName); if (relatedJPAEntityMap != null && relatedJPAEntityMap.containsKey(navigationPropertyName)) { oDataEntry.getProperties().get(navigationPropertyName); JPALink.linkJPAEntities(relatedJPAEntityMap.get(navigationPropertyName), jpaEntity, navProperty); continue; } // The second condition is required to ensure that there is an explicit request to link // two entities. Else the third condition will always be true for cases where two navigations // point to same entity types. if (parentJPAEntity != null && navProperty.getRelationship().equals(getViaNavigationProperty().getRelationship())) { List<Object> targetJPAEntities = new ArrayList<Object>(); targetJPAEntities.add(parentJPAEntity.getJPAEntity()); JPALink.linkJPAEntities(targetJPAEntities, jpaEntity, navProperty); } else if (!entryMetadata.getAssociationUris(navigationPropertyName).isEmpty()) { if (!relatedJPAEntityLink.contains(navigationPropertyName)) { relatedJPAEntityLink.add(navigationPropertyName); } } } } if (!relatedJPAEntityLink.isEmpty()) { JPALink link = new JPALink(oDataJPAContext); link.setSourceJPAEntity(jpaEntity); link.create(oDataEntitySet, oDataEntry, relatedJPAEntityLink); } } catch (EdmException e) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } catch (ODataJPAModelException e) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } } public EdmEntitySet getEdmEntitySet() { return oDataEntitySet; } public void create(final Map<String, Object> oDataEntryProperties) throws ODataJPARuntimeException { normalizeInlineEntries(oDataEntryProperties); write(oDataEntryProperties, true); } public void update(final ODataEntry oDataEntry) throws ODataJPARuntimeException { if (oDataEntry == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL, null); } Map<String, Object> oDataEntryProperties = oDataEntry.getProperties(); if (oDataEntry.containsInlineEntry()) { normalizeInlineEntries(oDataEntryProperties); } write(oDataEntryProperties, false); JPALink link = new JPALink(oDataJPAContext); link.setSourceJPAEntity(jpaEntity); try { link.create(oDataEntitySet, oDataEntry, oDataEntityType.getNavigationPropertyNames()); } catch (EdmException e) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } catch (ODataJPAModelException e) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } } public void update(final Map<String, Object> oDataEntryProperties) throws ODataJPARuntimeException { normalizeInlineEntries(oDataEntryProperties); write(oDataEntryProperties, false); } public void setJPAEntity(final Object jpaEntity) { this.jpaEntity = jpaEntity; } protected void setComplexProperty(Method accessModifier, final Object jpaEntity, final EdmStructuralType edmComplexType, final HashMap<String, Object> propertyValue) throws EdmException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, ODataJPARuntimeException, NoSuchMethodException, SecurityException, SQLException { setComplexProperty(accessModifier, jpaEntity, edmComplexType, propertyValue, null); } protected void setProperty(final Method method, final Object entity, final Object entityPropertyValue, final EdmSimpleType type) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ODataJPARuntimeException { setProperty(method, entity, entityPropertyValue, type, null); } protected void setEmbeddableKeyProperty(final HashMap<String, String> embeddableKeys, final List<EdmProperty> oDataEntryKeyProperties, final Map<String, Object> oDataEntryProperties, final Object entity) throws ODataJPARuntimeException, EdmException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { HashMap<String, Object> embeddableObjMap = new HashMap<String, Object>(); List<EdmProperty> leftODataEntryKeyProperties = new ArrayList<EdmProperty>(); HashMap<String, String> leftEmbeddableKeys = new HashMap<String, String>(); for (EdmProperty edmProperty : oDataEntryKeyProperties) { if (oDataEntryProperties.containsKey(edmProperty.getName()) == false) { continue; } String edmPropertyName = edmProperty.getName(); String embeddableKeyNameComposite = embeddableKeys.get(edmPropertyName); if (embeddableKeyNameComposite == null) { continue; } String embeddableKeyNameSplit[] = embeddableKeyNameComposite.split("\\."); String methodPartName = null; Method method = null; Object embeddableObj = null; if (embeddableObjMap.containsKey(embeddableKeyNameSplit[0]) == false) { methodPartName = embeddableKeyNameSplit[0]; method = jpaEntityParser.getAccessModifierSet(entity, methodPartName); embeddableObj = method.getParameterTypes()[0].newInstance(); method.invoke(entity, embeddableObj); embeddableObjMap.put(embeddableKeyNameSplit[0], embeddableObj); } else { embeddableObj = embeddableObjMap.get(embeddableKeyNameSplit[0]); } if (embeddableKeyNameSplit.length == 2) { methodPartName = embeddableKeyNameSplit[1]; method = jpaEntityParser.getAccessModifierSet(embeddableObj, methodPartName); Object simpleObj = oDataEntryProperties.get(edmProperty.getName()); method.invoke(embeddableObj, simpleObj); } else if (embeddableKeyNameSplit.length > 2) { // Deeply nested leftODataEntryKeyProperties.add(edmProperty); leftEmbeddableKeys .put(edmPropertyName, embeddableKeyNameComposite.split(embeddableKeyNameSplit[0] + ".", 2)[1]); } } } protected Object instantiateJPAEntity() throws InstantiationException, IllegalAccessException { if (jpaType == null) { throw new InstantiationException(); } return jpaType.newInstance(); } private void normalizeInlineEntries(final Map<String, Object> oDataEntryProperties) throws ODataJPARuntimeException { List<ODataEntry> entries = null; try { for (String navigationPropertyName : oDataEntityType.getNavigationPropertyNames()) { Object inline = oDataEntryProperties.get(navigationPropertyName); if (inline instanceof ODataFeed) { entries = ((ODataFeed) inline).getEntries(); } else if (inline instanceof ODataEntry) { entries = new ArrayList<ODataEntry>(); entries.add((ODataEntry) inline); } if (entries != null) { oDataEntryProperties.put(navigationPropertyName, entries); entries = null; } } } catch (EdmException e) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } } @SuppressWarnings("unchecked") private void write(final Map<String, Object> oDataEntryProperties, final boolean isCreate) throws ODataJPARuntimeException { try { EdmStructuralType structuralType = null; final List<String> keyNames = oDataEntityType.getKeyPropertyNames(); if (isCreate) { jpaEntity = instantiateJPAEntity(); } else if (jpaEntity == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.RESOURCE_NOT_FOUND, null); } if (accessModifiersWrite == null) { accessModifiersWrite = jpaEntityParser.getAccessModifiers(jpaEntity, oDataEntityType, JPAEntityParser.ACCESS_MODIFIER_SET); } if (oDataEntityType == null || oDataEntryProperties == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL, null); } final HashMap<String, String> embeddableKeys = jpaEntityParser.getJPAEmbeddableKeyMap(jpaEntity.getClass().getName()); Set<String> propertyNames = null; if (embeddableKeys != null) { setEmbeddableKeyProperty(embeddableKeys, oDataEntityType.getKeyProperties(), oDataEntryProperties, jpaEntity); propertyNames = new HashSet<String>(); propertyNames.addAll(oDataEntryProperties.keySet()); for (String key : embeddableKeys.keySet()) { propertyNames.remove(key); } } else { propertyNames = oDataEntryProperties.keySet(); } boolean isVirtual = false; for (String propertyName : propertyNames) { EdmTyped edmTyped = (EdmTyped) oDataEntityType.getProperty(propertyName); if (edmTyped instanceof EdmProperty) { isVirtual = ((JPAEdmMappingImpl)((EdmProperty) edmTyped).getMapping()).isVirtualAccess(); } else { isVirtual = false; } Method accessModifier = null; switch (edmTyped.getType().getKind()) { case SIMPLE: if (isCreate == false) { if (keyNames.contains(edmTyped.getName())) { continue; } } accessModifier = accessModifiersWrite.get(propertyName); if (isVirtual) { setProperty(accessModifier, jpaEntity, oDataEntryProperties.get(propertyName), (EdmSimpleType) edmTyped .getType(), propertyName); } else { setProperty(accessModifier, jpaEntity, oDataEntryProperties.get(propertyName), (EdmSimpleType) edmTyped .getType()); } break; case COMPLEX: structuralType = (EdmStructuralType) edmTyped.getType(); accessModifier = accessModifiersWrite.get(propertyName); if (isVirtual) { setComplexProperty(accessModifier, jpaEntity, structuralType, (HashMap<String, Object>) oDataEntryProperties.get(propertyName), propertyName); } else { setComplexProperty(accessModifier, jpaEntity, structuralType, (HashMap<String, Object>) oDataEntryProperties.get(propertyName)); } break; case NAVIGATION: case ENTITY: if (isCreate) { structuralType = (EdmStructuralType) edmTyped.getType(); EdmNavigationProperty navProperty = (EdmNavigationProperty) edmTyped; EdmEntitySet edmRelatedEntitySet = oDataEntitySet.getRelatedEntitySet(navProperty); List<ODataEntry> relatedEntries = (List<ODataEntry>) oDataEntryProperties.get(propertyName); if (relatedJPAEntityMap == null) { relatedJPAEntityMap = new HashMap<String, List<Object>>(); } List<Object> relatedJPAEntities = new ArrayList<Object>(); for (ODataEntry oDataEntry : relatedEntries) { JPAEntity relatedEntity = new JPAEntity((EdmEntityType) structuralType, edmRelatedEntitySet, oDataJPAContext); relatedEntity.setParentJPAEntity(this); relatedEntity.setViaNavigationProperty(navProperty); relatedEntity.create(oDataEntry); if (oDataEntry.getProperties().size() == 0) { if (!oDataEntry.getMetadata().getUri().isEmpty() && !relatedJPAEntityLink.contains(navProperty.getName())) { relatedJPAEntityLink.add(navProperty.getName()); } } else { relatedJPAEntities.add(relatedEntity.getJPAEntity()); } } if (!relatedJPAEntities.isEmpty()) { relatedJPAEntityMap.put(navProperty.getName(), relatedJPAEntities); } } default: continue; } } } catch (Exception e) { if (e instanceof ODataJPARuntimeException) { throw (ODataJPARuntimeException) e; } throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.GENERAL .addContent(e.getMessage()), e); } } @SuppressWarnings("unchecked") protected void setComplexProperty(Method accessModifier, final Object jpaEntity, final EdmStructuralType edmComplexType, final HashMap<String, Object> propertyValue, String propertyName) throws EdmException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, ODataJPARuntimeException, NoSuchMethodException, SecurityException, SQLException { JPAEdmMapping mapping = (JPAEdmMapping) edmComplexType.getMapping(); Object embeddableObject = mapping.getJPAType().newInstance(); if (propertyName != null) { accessModifier.invoke(jpaEntity, propertyName, embeddableObject); } else { accessModifier.invoke(jpaEntity, embeddableObject); } HashMap<String, Method> accessModifiers = jpaEntityParser.getAccessModifiers(embeddableObject, edmComplexType, JPAEntityParser.ACCESS_MODIFIER_SET); for (String edmPropertyName : edmComplexType.getPropertyNames()) { if (propertyValue != null) { EdmTyped edmTyped = edmComplexType.getProperty(edmPropertyName); accessModifier = accessModifiers.get(edmPropertyName); EdmType type = edmTyped.getType(); if (type.getKind().toString().equals(EdmTypeKind.COMPLEX.toString())) { setComplexProperty(accessModifier, embeddableObject, (EdmStructuralType) type, (HashMap<String, Object>) propertyValue.get(edmPropertyName), propertyName); } else { EdmSimpleType simpleType = (EdmSimpleType) type; if (propertyName != null) { setProperty(accessModifier, embeddableObject, propertyValue.get(edmPropertyName), simpleType, edmPropertyName); } else { setProperty(accessModifier, embeddableObject, propertyValue.get(edmPropertyName), simpleType); } } } } } @SuppressWarnings({ "unchecked", "rawtypes" }) protected void setProperty(final Method method, final Object entity, final Object entityPropertyValue, final EdmSimpleType type, String propertyName) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ODataJPARuntimeException { if (entityPropertyValue != null) { if (propertyName != null) { method.invoke(entity, propertyName, entityPropertyValue); return; } Class<?> parameterType = method.getParameterTypes()[0]; if (type != null && type.getDefaultType().equals(String.class)) { if (parameterType.equals(String.class)) { method.invoke(entity, entityPropertyValue); } else if (parameterType.equals(char[].class)) { char[] characters = ((String) entityPropertyValue).toCharArray(); method.invoke(entity, characters); } else if (parameterType.equals(char.class)) { char c = ((String) entityPropertyValue).charAt(0); method.invoke(entity, c); } else if (parameterType.equals(Character[].class)) { Character[] characters = JPAEntityParser.toCharacterArray((String) entityPropertyValue); method.invoke(entity, (Object) characters); } else if (parameterType.equals(Character.class)) { Character c = Character.valueOf(((String) entityPropertyValue).charAt(0)); method.invoke(entity, c); } else if (parameterType.isEnum()) { Enum e = Enum.valueOf((Class<Enum>) parameterType, (String) entityPropertyValue); method.invoke(entity, e); } } else if (parameterType.equals(Blob.class)) { if (onJPAWriteContent == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.ERROR_JPA_BLOB_NULL, null); } else { method.invoke(entity, onJPAWriteContent.getJPABlob((byte[]) entityPropertyValue)); } } else if (parameterType.equals(Clob.class)) { if (onJPAWriteContent == null) { throw ODataJPARuntimeException .throwException(ODataJPARuntimeException.ERROR_JPA_CLOB_NULL, null); } else { method.invoke(entity, onJPAWriteContent.getJPAClob(((String) entityPropertyValue).toCharArray())); } } else if (parameterType.equals(Timestamp.class)) { Timestamp ts = new Timestamp(((Calendar) entityPropertyValue).getTimeInMillis()); method.invoke(entity, ts); } else if (parameterType.equals(java.util.Date.class)) { method.invoke(entity, ((Calendar) entityPropertyValue).getTime()); } else if (parameterType.equals(java.sql.Date.class)) { long timeInMs = ((Calendar) entityPropertyValue).getTimeInMillis(); method.invoke(entity, new java.sql.Date(timeInMs)); } else if (parameterType.equals(java.sql.Time.class)) { long timeInMs = ((Calendar) entityPropertyValue).getTimeInMillis(); method.invoke(entity, new java.sql.Time(timeInMs)); } else { method.invoke(entity, entityPropertyValue); } } } }