/*******************************************************************************
* Copyright 2013 SAP AG
*
* Licensed 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 com.sap.core.odata.processor.core.jpa.access.data;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.sap.core.odata.api.edm.EdmEntitySet;
import com.sap.core.odata.api.edm.EdmEntityType;
import com.sap.core.odata.api.edm.EdmException;
import com.sap.core.odata.api.edm.EdmMultiplicity;
import com.sap.core.odata.api.ep.entry.ODataEntry;
import com.sap.core.odata.api.uri.info.DeleteUriInfo;
import com.sap.core.odata.api.uri.info.GetEntityCountUriInfo;
import com.sap.core.odata.api.uri.info.GetEntityLinkUriInfo;
import com.sap.core.odata.api.uri.info.GetEntitySetCountUriInfo;
import com.sap.core.odata.api.uri.info.GetEntitySetLinksUriInfo;
import com.sap.core.odata.api.uri.info.GetEntitySetUriInfo;
import com.sap.core.odata.api.uri.info.GetEntityUriInfo;
import com.sap.core.odata.api.uri.info.GetFunctionImportUriInfo;
import com.sap.core.odata.api.uri.info.PostUriInfo;
import com.sap.core.odata.api.uri.info.PutMergePatchUriInfo;
import com.sap.core.odata.processor.api.jpa.ODataJPAContext;
import com.sap.core.odata.processor.api.jpa.access.JPAFunction;
import com.sap.core.odata.processor.api.jpa.access.JPAMethodContext;
import com.sap.core.odata.processor.api.jpa.access.JPAProcessor;
import com.sap.core.odata.processor.api.jpa.exception.ODataJPAModelException;
import com.sap.core.odata.processor.api.jpa.exception.ODataJPARuntimeException;
import com.sap.core.odata.processor.api.jpa.jpql.JPQLContext;
import com.sap.core.odata.processor.api.jpa.jpql.JPQLContextType;
import com.sap.core.odata.processor.api.jpa.jpql.JPQLStatement;
import com.sap.core.odata.processor.core.jpa.ODataEntityParser;
public class JPAProcessorImpl implements JPAProcessor {
ODataJPAContext oDataJPAContext;
EntityManager em;
public JPAProcessorImpl(final ODataJPAContext oDataJPAContext) {
this.oDataJPAContext = oDataJPAContext;
em = oDataJPAContext.getEntityManager();
}
/* Process Function Import Request */
@SuppressWarnings("unchecked")
@Override
public List<Object> process(final GetFunctionImportUriInfo uriParserResultView)
throws ODataJPAModelException, ODataJPARuntimeException {
JPAMethodContext jpaMethodContext = JPAMethodContext.createBuilder(
JPQLContextType.FUNCTION, uriParserResultView).build();
List<Object> resultObj = null;
try {
JPAFunction jpaFunction = jpaMethodContext.getJPAFunctionList()
.get(0);
Method method = jpaFunction.getFunction();
Object[] args = jpaFunction.getArguments();
if (uriParserResultView.getFunctionImport().getReturnType()
.getMultiplicity().equals(EdmMultiplicity.MANY)) {
resultObj = (List<Object>) method.invoke(
jpaMethodContext.getEnclosingObject(), args);
} else {
resultObj = new ArrayList<Object>();
Object result = method.invoke(
jpaMethodContext.getEnclosingObject(), args);
resultObj.add(result);
}
} catch (EdmException e) {
throw ODataJPARuntimeException
.throwException(ODataJPARuntimeException.GENERAL
.addContent(e.getMessage()), e);
} catch (IllegalAccessException e) {
throw ODataJPARuntimeException
.throwException(ODataJPARuntimeException.GENERAL
.addContent(e.getMessage()), e);
} catch (IllegalArgumentException e) {
throw ODataJPARuntimeException
.throwException(ODataJPARuntimeException.GENERAL
.addContent(e.getMessage()), e);
} catch (InvocationTargetException e) {
throw ODataJPARuntimeException
.throwException(ODataJPARuntimeException.GENERAL
.addContent(e.getTargetException().getMessage()), e.getTargetException());
}
return resultObj;
}
/* Process Get Entity Set Request (Query) */
@SuppressWarnings("unchecked")
@Override
public <T> List<T> process(final GetEntitySetUriInfo uriParserResultView)
throws ODataJPAModelException, ODataJPARuntimeException {
if (uriParserResultView.getFunctionImport() != null) {
return (List<T>) process((GetFunctionImportUriInfo) uriParserResultView);
}
JPQLContextType contextType = null;
try {
if (!uriParserResultView.getStartEntitySet().getName()
.equals(uriParserResultView.getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN;
} else {
contextType = JPQLContextType.SELECT;
}
} catch (EdmException e) {
ODataJPARuntimeException.throwException(
ODataJPARuntimeException.GENERAL, e);
}
JPQLContext jpqlContext = JPQLContext.createBuilder(contextType,
uriParserResultView).build();
JPQLStatement jpqlStatement = JPQLStatement.createBuilder(jpqlContext)
.build();
Query query = null;
try {
query = em.createQuery(jpqlStatement.toString());
// $top/$skip with $inlinecount case handled in response builder to avoid multiple DB call
if (uriParserResultView.getSkip() != null && uriParserResultView.getInlineCount() == null) {
query.setFirstResult(uriParserResultView.getSkip());
}
if (uriParserResultView.getTop() != null && uriParserResultView.getInlineCount() == null) {
if (uriParserResultView.getTop() == 0) {
List<T> resultList = new ArrayList<T>();
return resultList;
} else {
query.setMaxResults(uriParserResultView.getTop());
}
}
return query.getResultList();
} catch (Exception e) {
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_QUERY_CREATE, e);
}
}
/* Process Get Entity Request (Read) */
@Override
public <T> Object process(GetEntityUriInfo uriParserResultView)
throws ODataJPAModelException, ODataJPARuntimeException {
JPQLContextType contextType = null;
try {
if (uriParserResultView instanceof GetEntityUriInfo) {
uriParserResultView = ((GetEntityUriInfo) uriParserResultView);
if (!((GetEntityUriInfo) uriParserResultView).getStartEntitySet().getName()
.equals(((GetEntityUriInfo) uriParserResultView).getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN_SINGLE;
} else {
contextType = JPQLContextType.SELECT_SINGLE;
}
}
} catch (EdmException e) {
ODataJPARuntimeException.throwException(
ODataJPARuntimeException.GENERAL, e);
}
return readEntity(uriParserResultView, contextType);
}
/* Process $count for Get Entity Set Request */
@Override
public long process(final GetEntitySetCountUriInfo resultsView)
throws ODataJPAModelException, ODataJPARuntimeException {
JPQLContextType contextType = null;
try {
if (!resultsView.getStartEntitySet().getName()
.equals(resultsView.getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN_COUNT;
} else {
contextType = JPQLContextType.SELECT_COUNT;
}
} catch (EdmException e) {
ODataJPARuntimeException.throwException(
ODataJPARuntimeException.GENERAL, e);
}
JPQLContext jpqlContext = JPQLContext.createBuilder(contextType,
resultsView).build();
JPQLStatement jpqlStatement = JPQLStatement.createBuilder(jpqlContext)
.build();
Query query = null;
try {
query = em.createQuery(jpqlStatement.toString());
List<?> resultList = query.getResultList();
if (resultList != null && resultList.size() == 1) {
return Long.valueOf(resultList.get(0).toString());
}
} catch (IllegalArgumentException e) {
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_QUERY_CREATE, e);
}
return 0;
}
/* Process $count for Get Entity Request */
@Override
public long process(final GetEntityCountUriInfo resultsView) throws ODataJPAModelException, ODataJPARuntimeException {
JPQLContextType contextType = null;
try {
if (!resultsView.getStartEntitySet().getName()
.equals(resultsView.getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN_COUNT;
} else {
contextType = JPQLContextType.SELECT_COUNT;
}
} catch (EdmException e) {
ODataJPARuntimeException.throwException(
ODataJPARuntimeException.GENERAL, e);
}
JPQLContext jpqlContext = JPQLContext.createBuilder(contextType,
resultsView).build();
JPQLStatement jpqlStatement = JPQLStatement.createBuilder(jpqlContext)
.build();
Query query = null;
try {
query = em.createQuery(jpqlStatement.toString());
List<?> resultList = query.getResultList();
if (resultList != null && resultList.size() == 1) {
return Long.valueOf(resultList.get(0).toString());
}
} catch (IllegalArgumentException e) {
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_QUERY_CREATE, e);
}
return 0;
}
/* Process Create Entity Request */
@Override
public <T> List<T> process(final PostUriInfo createView, final InputStream content,
final String requestedContentType) throws ODataJPAModelException,
ODataJPARuntimeException {
return processCreate(createView, content, null, requestedContentType);
}
@Override
public <T> List<T> process(final PostUriInfo createView, final Map<String, Object> content) throws ODataJPAModelException, ODataJPARuntimeException {
return processCreate(createView, null, content, null);
}
/* Process Update Entity Request */
@Override
public <T> Object process(final PutMergePatchUriInfo updateView,
final InputStream content, final String requestContentType)
throws ODataJPAModelException, ODataJPARuntimeException {
return processUpdate(updateView, content, null, requestContentType);
}
@Override
public <T> Object process(final PutMergePatchUriInfo updateView, final Map<String, Object> content) throws ODataJPAModelException, ODataJPARuntimeException {
return processUpdate(updateView, null, content, null);
}
@SuppressWarnings("unchecked")
private <T> List<T> processCreate(final PostUriInfo createView, final InputStream content, final Map<String, Object> properties,
final String requestedContentType) throws ODataJPAModelException,
ODataJPARuntimeException {
try {
final EdmEntitySet oDataEntitySet = createView.getTargetEntitySet();
final EdmEntityType oDataEntityType = oDataEntitySet.getEntityType();
final JPAEntity virtualJPAEntity = new JPAEntity(oDataEntityType, oDataEntitySet);
final List<Object> createList = new ArrayList<Object>();
Object jpaEntity = null;
if (content != null) {
final ODataEntityParser oDataEntityParser = new ODataEntityParser(oDataJPAContext);
final ODataEntry oDataEntry = oDataEntityParser.parseEntry(oDataEntitySet, content, requestedContentType, false);
virtualJPAEntity.create(oDataEntry);
JPALink link = new JPALink(oDataJPAContext);
link.setSourceJPAEntity(jpaEntity);
link.create(createView, content, requestedContentType, requestedContentType);
}
else if (properties != null) {
virtualJPAEntity.create(properties);
} else {
return null;
}
em.getTransaction().begin();
jpaEntity = virtualJPAEntity.getJPAEntity();
em.persist(jpaEntity);
if (em.contains(jpaEntity)) {
em.getTransaction().commit();
createList.add(virtualJPAEntity.getJPAEntity());
createList.add(virtualJPAEntity.getInlineJPAEntities());
return (List<T>) createList;
}
} catch (Exception e) {
em.getTransaction().rollback();
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_CREATE_REQUEST, e);
}
return null;
}
public <T> Object processUpdate(PutMergePatchUriInfo updateView,
final InputStream content, final Map<String, Object> properties, final String requestContentType)
throws ODataJPAModelException, ODataJPARuntimeException {
JPQLContextType contextType = null;
Object jpaEntity = null;
try {
em.getTransaction().begin();
if (updateView instanceof PutMergePatchUriInfo) {
updateView = ((PutMergePatchUriInfo) updateView);
if (!((PutMergePatchUriInfo) updateView).getStartEntitySet().getName()
.equals(((PutMergePatchUriInfo) updateView).getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN_SINGLE;
} else {
contextType = JPQLContextType.SELECT_SINGLE;
}
}
jpaEntity = readEntity(updateView, contextType);
if (jpaEntity == null) {
throw ODataJPARuntimeException
.throwException(ODataJPARuntimeException.RESOURCE_NOT_FOUND, null);
}
final EdmEntitySet oDataEntitySet = updateView.getTargetEntitySet();
final EdmEntityType oDataEntityType = oDataEntitySet.getEntityType();
final JPAEntity virtualJPAEntity = new JPAEntity(oDataEntityType, oDataEntitySet);
virtualJPAEntity.setJPAEntity(jpaEntity);
if (content != null) {
final ODataEntityParser oDataEntityParser = new ODataEntityParser(oDataJPAContext);
final ODataEntry oDataEntry = oDataEntityParser.parseEntry(oDataEntitySet, content, requestContentType, false);
virtualJPAEntity.update(oDataEntry);
}
else if (properties != null) {
virtualJPAEntity.update(properties);
} else {
return null;
}
em.flush();
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_UPDATE_REQUEST, e);
}
return jpaEntity;
}
/* Process Delete Entity Request */
@Override
public Object process(DeleteUriInfo uriParserResultView, final String contentType)
throws ODataJPAModelException, ODataJPARuntimeException {
JPQLContextType contextType = null;
try {
if (uriParserResultView instanceof DeleteUriInfo) {
uriParserResultView = ((DeleteUriInfo) uriParserResultView);
if (!((DeleteUriInfo) uriParserResultView).getStartEntitySet().getName()
.equals(((DeleteUriInfo) uriParserResultView).getTargetEntitySet().getName())) {
contextType = JPQLContextType.JOIN_SINGLE;
} else {
contextType = JPQLContextType.SELECT_SINGLE;
}
}
} catch (EdmException e) {
ODataJPARuntimeException.throwException(
ODataJPARuntimeException.GENERAL, e);
}
// First read the entity with read operation.
Object selectedObject = readEntity(uriParserResultView, contextType);
// Read operation done. This object would be passed on to entity manager for delete
if (selectedObject != null) {
try {
em.getTransaction().begin();
em.remove(selectedObject);
em.flush();
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_DELETE_REQUEST, e);
}
}
return selectedObject;
}
/* Process Get Entity Link Request */
@Override
public Object process(final GetEntityLinkUriInfo uriParserResultView)
throws ODataJPAModelException, ODataJPARuntimeException {
return this.process((GetEntityUriInfo) uriParserResultView);
}
/* Process Get Entity Set Link Request */
@Override
public <T> List<T> process(final GetEntitySetLinksUriInfo uriParserResultView)
throws ODataJPAModelException, ODataJPARuntimeException {
return this.process((GetEntitySetUriInfo) uriParserResultView);
}
@Override
public void process(final PostUriInfo uriInfo,
final InputStream content, final String requestContentType, final String contentType)
throws ODataJPARuntimeException, ODataJPAModelException {
JPALink link = new JPALink(oDataJPAContext);
link.create(uriInfo, content, requestContentType, contentType);
link.save();
}
/* Common method for Read and Delete */
private Object readEntity(final Object uriParserResultView, final JPQLContextType contextType)
throws ODataJPAModelException, ODataJPARuntimeException {
Object selectedObject = null;
if (uriParserResultView instanceof DeleteUriInfo || uriParserResultView instanceof GetEntityUriInfo || uriParserResultView instanceof PutMergePatchUriInfo) {
JPQLContext selectJPQLContext = JPQLContext.createBuilder(
contextType, uriParserResultView).build();
JPQLStatement selectJPQLStatement = JPQLStatement.createBuilder(
selectJPQLContext).build();
Query query = null;
try {
query = em.createQuery(selectJPQLStatement.toString());
if (!query.getResultList().isEmpty()) {
selectedObject = query.getResultList().get(0);
}
} catch (IllegalArgumentException e) {
throw ODataJPARuntimeException.throwException(
ODataJPARuntimeException.ERROR_JPQL_QUERY_CREATE, e);
}
}
return selectedObject;
}
@Override
public void process(final PutMergePatchUriInfo putUriInfo,
final InputStream content, final String requestContentType, final String contentType)
throws ODataJPARuntimeException, ODataJPAModelException {
JPALink link = new JPALink(oDataJPAContext);
link.update(putUriInfo, content, requestContentType, contentType);
link.save();
}
}