/******************************************************************************* * Copyright (c) 2014, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Dmitry Kornilov - Initial implementation ******************************************************************************/ package org.eclipse.persistence.jpa.rs.util; import org.eclipse.persistence.dynamic.DynamicClassLoader; import org.eclipse.persistence.internal.jpa.rs.metadata.model.LinkV2; import org.eclipse.persistence.internal.jpa.weaving.CollectionProxyClassWriter; import org.eclipse.persistence.internal.weaving.PersistenceWeavedRest; import org.eclipse.persistence.jpa.rs.PersistenceContext; import org.eclipse.persistence.jpa.rs.ReservedWords; import org.eclipse.persistence.jpa.rs.exceptions.JPARSException; import org.eclipse.persistence.jpa.rs.util.list.PageableCollection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Helper class used to create proxies for collections. Used in JPARS 2.0. * * @author Dmitry Kornilov * @since EclipseLink 2.6.0 */ public class CollectionWrapperBuilder { private final PersistenceContext context; /** * Creates a new CollectionWrapperBuilder. * * @param context the persistence context. */ public CollectionWrapperBuilder(PersistenceContext context) { this.context = context; } /** * Builds proxies for collections containing in a given object. Replaces original collections with * newly created proxies. * * @param object the object to wrap collections. */ public void wrapCollections(Object object) { if (object instanceof PageableCollection) { for (Object o : ((PageableCollection) object).getItems()) { wrapCollectionsForEntity(o); } } else { wrapCollectionsForEntity(object); } } private void wrapCollectionsForEntity(Object entity) { if (!PersistenceWeavedRest.class.isAssignableFrom(entity.getClass())) { return; } for (final Field field : entity.getClass().getDeclaredFields()) { if (Collection.class.isAssignableFrom(field.getType())) { // Get entity id final String id = IdHelper.stringifyId(entity, entity.getClass().getSimpleName(), context); // Generate links final List<LinkV2> links = new ArrayList<LinkV2>(2); final String href = HrefHelper.buildEntityFieldHref(context, entity.getClass().getSimpleName(), id, field.getName()); links.add(new LinkV2(ReservedWords.JPARS_REL_SELF, href)); links.add(new LinkV2(ReservedWords.JPARS_REL_CANONICAL, href)); // Get accessibility boolean accessible = field.isAccessible(); if (!accessible) { field.setAccessible(true); } // Make proxy try { // No proxy for weaved fields and null fields if (!field.getName().startsWith("_") && field.get(entity) != null) { CollectionProxy proxy = getRestCollectionProxy((Collection) field.get(entity), entity.getClass().getName(), field.getName()); proxy.setLinks(links); field.set(entity, field.getType().cast(proxy)); } } catch (IllegalAccessException e) { throw JPARSException.exceptionOccurred(e); } // Restore accessibility if (!accessible) { field.setAccessible(false); } } } } private CollectionProxy getRestCollectionProxy(final Collection toProxy, final String entityName, final String fieldname) { try { final DynamicClassLoader classLoader = (DynamicClassLoader)context.getServerSession().getDatasourcePlatform().getConversionManager().getLoader(); final CollectionProxyClassWriter writer = new CollectionProxyClassWriter(toProxy.getClass().getName(), entityName, fieldname); final String proxyClassName = writer.getClassName(); if (classLoader.getClassWriter(proxyClassName) == null) { classLoader.addClass(proxyClassName, writer); } final Class referenceAdaptorClass = Class.forName(proxyClassName, true, classLoader); final Class[] argTypes = {Collection.class}; final Constructor referenceAdaptorConstructor = referenceAdaptorClass.getDeclaredConstructor(argTypes); final Object[] args = new Object[]{toProxy}; return (CollectionProxy)referenceAdaptorConstructor.newInstance(args); } catch (Exception e) { throw JPARSException.exceptionOccurred(e); } } }