/* * 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.deltaspike.data.impl.graph; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.persistence.EntityManager; import org.apache.deltaspike.core.util.ClassUtils; import org.apache.deltaspike.core.util.ExceptionUtils; import org.apache.deltaspike.data.api.EntityGraph; /** * Helper for invoking methods related to entity graphs by reflection. * <p> * Reflection is required to stay compatible with JPA 2.0. */ public final class EntityGraphHelper { private static final Class<?> ENTITY_GRAPH_CLASS; private static final Class<?> SUBGRAPH_CLASS; private static final Method EG_ADD_ATTRIBUTE_NODES; private static final Method EG_ADD_SUBGRAPH; private static final Method SUBGRAPH_ADD_ATTRIBUTE_NODES; private static final Method SUBGRAPH_ADD_SUBGRAPH; private static final Method EM_GET_ENTITY_GRAPH; private static final Method EM_CREATE_ENTITY_GRAPH; static { ENTITY_GRAPH_CLASS = ClassUtils.tryToLoadClassForName("javax.persistence.EntityGraph"); SUBGRAPH_CLASS = ClassUtils.tryToLoadClassForName("javax.persistence.Subgraph"); if (ENTITY_GRAPH_CLASS == null) { EG_ADD_ATTRIBUTE_NODES = null; EG_ADD_SUBGRAPH = null; SUBGRAPH_ADD_ATTRIBUTE_NODES = null; SUBGRAPH_ADD_SUBGRAPH = null; EM_GET_ENTITY_GRAPH = null; EM_CREATE_ENTITY_GRAPH = null; } else { try { EG_ADD_ATTRIBUTE_NODES = ENTITY_GRAPH_CLASS.getMethod("addAttributeNodes", String[].class); EG_ADD_SUBGRAPH = ENTITY_GRAPH_CLASS.getMethod("addSubgraph", String.class); SUBGRAPH_ADD_ATTRIBUTE_NODES = SUBGRAPH_CLASS.getMethod("addAttributeNodes", String[].class); SUBGRAPH_ADD_SUBGRAPH = SUBGRAPH_CLASS.getMethod("addSubgraph", String.class); EM_GET_ENTITY_GRAPH = EntityManager.class.getMethod("getEntityGraph", String.class); EM_CREATE_ENTITY_GRAPH = EntityManager.class.getMethod("createEntityGraph", Class.class); } catch (NoSuchMethodException e) { throw ExceptionUtils.throwAsRuntimeException(e); } } } private EntityGraphHelper() { // hidden constructor } public static boolean isAvailable() { return ENTITY_GRAPH_CLASS != null; } /* * TODO Check if this can be replaced by org.apache.deltaspike.core.util.ReflectionUtils.invokeMethod. * This does not work at the moment due to an issue with exception handling in that method * which needs further analysis. */ private static Object uncheckedInvoke(Method method, Object target, Object... args) { try { return method.invoke(target, args); } catch (IllegalAccessException e) { throw ExceptionUtils.throwAsRuntimeException(e); } catch (InvocationTargetException e) { throw ExceptionUtils.throwAsRuntimeException(e.getCause()); } } public static Object getEntityGraph(EntityManager em, Class<?> entityClass, EntityGraph entityGraphAnn) { ensureAvailable(); String graphName = entityGraphAnn.value(); if (graphName.isEmpty()) { return buildEntityGraph(em, entityClass, entityGraphAnn.paths()); } else { return uncheckedInvoke(EM_GET_ENTITY_GRAPH, em, graphName); } } private static Object createEntityGraph(EntityManager em, Class<?> entityClass) { return uncheckedInvoke(EM_CREATE_ENTITY_GRAPH, em, entityClass); } private static void ensureAvailable() { if (!isAvailable()) { throw new EntityGraphException("Class java.persistence.EntityGraph is not available. " + "Does your PersistenceProvider support JPA 2.1?"); } } private static Object addSubgraph(Object graph, String attributeName) { if (ENTITY_GRAPH_CLASS.isInstance(graph)) { return uncheckedInvoke(EG_ADD_SUBGRAPH, graph, attributeName); } else if (SUBGRAPH_CLASS.isInstance(graph)) { return uncheckedInvoke(SUBGRAPH_ADD_SUBGRAPH, graph, attributeName); } return null; } private static void addAttributeNodes(Object graph, String attributeName) { if (ENTITY_GRAPH_CLASS.isInstance(graph)) { uncheckedInvoke(EG_ADD_ATTRIBUTE_NODES, graph, new Object[] { new String[] { attributeName } }); } else if (SUBGRAPH_CLASS.isInstance(graph)) { uncheckedInvoke(SUBGRAPH_ADD_ATTRIBUTE_NODES, graph, new Object[] { new String[] { attributeName } }); } } private static Object buildEntityGraph(EntityManager em, Class<?> entityClass, String[] attributePaths) { Object graph = createEntityGraph(em, entityClass); List<String> paths = new ArrayList<String>(Arrays.asList(attributePaths)); // handle longest paths first Collections.sort(paths); Collections.reverse(paths); for (String path : attributePaths) { if (path.contains(".")) { String[] segments = path.split("\\."); Object parent = addSubgraph(graph, segments[0]); for (int i = 1; i < segments.length - 1; i++) { addSubgraph(parent, segments[i]); } addAttributeNodes(parent, segments[segments.length - 1]); } else { addAttributeNodes(graph, path); } } return graph; } }