/******************************************************************************* * Copyright (c) 1998, 2016 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: * Oracle - initial API and implementation from Oracle TopLink * 19/04/2014-2.6 Lukas Jungmann * - 429992: JavaSE 8/ASM 5.0.1 support (EclipseLink silently ignores Entity classes with lambda expressions) ******************************************************************************/ package org.eclipse.persistence.internal.jpa.weaving; // J2SE imports import java.lang.instrument.IllegalClassFormatException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.ProtectionDomain; import java.util.Map; import javax.persistence.spi.ClassTransformer; import org.eclipse.persistence.config.SystemProperties; import org.eclipse.persistence.internal.helper.Helper; import org.eclipse.persistence.internal.libraries.asm.ClassReader; import org.eclipse.persistence.internal.libraries.asm.ClassVisitor; import org.eclipse.persistence.internal.libraries.asm.ClassWriter; import org.eclipse.persistence.internal.libraries.asm.commons.SerialVersionUIDAdder; import org.eclipse.persistence.internal.logging.StdErrLogger; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.internal.security.PrivilegedGetClassLoaderFromCurrentThread; import org.eclipse.persistence.logging.SessionLog; import org.eclipse.persistence.sessions.Session; /** * INTERNAL: * This class performs dynamic byte code weaving: for each attribute * mapped with One To One mapping with Basic Indirection it substitutes the * original attribute's type for ValueHolderInterface. */ public class PersistenceWeaver implements ClassTransformer { /** Class name in JVM '/' format to {@link ClassDetails} map. */ protected Map<String, ClassDetails> classDetailsMap; /** * INTERNAL: * Creates an instance of dynamic byte code weaver. * @param session EclipseLink session (not used so {@code null} value is OK). * @param classDetailsMap Class name to {@link ClassDetails} map. * @deprecated Session instance is no longer needed for logging. Will be removed in 2.8. */ public PersistenceWeaver(final Session session, final Map<String, ClassDetails> classDetailsMap) { this.classDetailsMap = classDetailsMap; } /** * INTERNAL: * Creates an instance of dynamic byte code weaver. * @param classDetailsMap Class name to {@link ClassDetails} map. * @since 2.7 */ public PersistenceWeaver(final Map<String, ClassDetails> classDetailsMap) { this.classDetailsMap = classDetailsMap; } /** * INTERNAL: * Allow the weaver to be clear to release its referenced memory. * This is required because the class loader reference to the transformer will never gc. */ public void clear() { this.classDetailsMap = null; } /** * INTERNAL: * Get Class name in JVM '/' format to {@link ClassDetails} map. * @return Class name in JVM '/' format to {@link ClassDetails} map. */ public Map<String, ClassDetails> getClassDetailsMap() { return classDetailsMap; } /** * INTERNAL: * Perform dynamic byte code weaving of class. * @param loader The defining loader of the class to be transformed, may be {@code null} * if the bootstrap loader. * @param className The name of the class in the internal form of fully qualified class and interface * names. * @param classBeingRedefined If this is a redefine, the class being redefined, otherwise {@code null}. * @param protectionDomain The protection domain of the class being defined or redefined. * @param classfileBuffer The input byte buffer in class file format (must not be modified). * @return A well-formed class file buffer (the result of the transform), or {@code null} if no transform * is performed. */ @Override public byte[] transform(final ClassLoader loader, final String className, final Class classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { // PERF: Is finest logging turned on? final boolean shouldLogFinest = StdErrLogger.shouldLog(SessionLog.FINEST, SessionLog.WEAVER); final Map classDetailsMap = this.classDetailsMap; // Check if cleared already. if (classDetailsMap == null) { return null; } try { /* * The ClassFileTransformer callback - when called by the JVM's * Instrumentation implementation - is invoked for every class loaded. * Thus, we must check the classDetailsMap to see if we are 'interested' * in the class. */ final ClassDetails classDetails = (ClassDetails)classDetailsMap.get(Helper.toSlashedClassName(className)); if (classDetails != null) { if (shouldLogFinest) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "begin_weaving_class", className); } final ClassReader classReader = new ClassReader(classfileBuffer); final String reflectiveIntrospectionProperty = PrivilegedAccessHelper.getSystemProperty(SystemProperties.WEAVING_REFLECTIVE_INTROSPECTION); final ClassWriter classWriter = reflectiveIntrospectionProperty != null ? new ClassWriter(ClassWriter.COMPUTE_FRAMES) : new ComputeClassWriter(loader, ClassWriter.COMPUTE_FRAMES); final ClassWeaver classWeaver = new ClassWeaver(classWriter, classDetails); final ClassVisitor sv = new SerialVersionUIDAdder(classWeaver); if (shouldLogFinest) { ClassLoader contextClassLoader; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { try { contextClassLoader = AccessController.doPrivileged( new PrivilegedGetClassLoaderFromCurrentThread()); } catch (PrivilegedActionException ex) { throw (RuntimeException) ex.getCause(); } } else { contextClassLoader = Thread.currentThread().getContextClassLoader(); } if (reflectiveIntrospectionProperty != null) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaving_init_class_writer", className, Integer.toHexString(System.identityHashCode(contextClassLoader))); } else { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaving_init_compute_class_writer", className, Integer.toHexString(System.identityHashCode(contextClassLoader)), loader != null ? Integer.toHexString(System.identityHashCode(loader)) : "null"); } } classReader.accept(sv, 0); if (classWeaver.alreadyWeaved) { if (shouldLogFinest) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "end_weaving_class", className); } return null; } if (classWeaver.weaved) { final byte[] bytes = classWriter.toByteArray(); final String outputPath = PrivilegedAccessHelper.getSystemProperty(SystemProperties.WEAVING_OUTPUT_PATH, ""); if (!outputPath.equals("")) { Helper.outputClassFile(className, bytes, outputPath); } // PERF: Don't execute this set of if statements with logging turned off. if (shouldLogFinest) { if (classWeaver.weavedPersistenceEntity) { StdErrLogger.log( SessionLog.FINEST, SessionLog.WEAVER, "weaved_persistenceentity", className); } if (classWeaver.weavedChangeTracker) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaved_changetracker", className); } if (classWeaver.weavedLazy) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaved_lazy", className); } if (classWeaver.weavedFetchGroups) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaved_fetchgroups", className); } if (classWeaver.weavedRest) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "weaved_rest", className); } StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "end_weaving_class", className); } return bytes; } if (shouldLogFinest) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "end_weaving_class", className); } } else { if (shouldLogFinest) { StdErrLogger.log( SessionLog.FINEST, SessionLog.WEAVER, "transform_missing_class_details", className); } } } catch (Throwable exception) { if (StdErrLogger.shouldLog(SessionLog.WARNING, SessionLog.WEAVER)) { StdErrLogger.log(SessionLog.WARNING, SessionLog.WEAVER, "exception_while_weaving", new Object[] {className, exception.getLocalizedMessage()}); if (shouldLogFinest) { StdErrLogger.logThrowable(SessionLog.FINEST, SessionLog.WEAVER, exception); } } } if (shouldLogFinest) { StdErrLogger.log(SessionLog.FINEST, SessionLog.WEAVER, "transform_existing_class_bytes", className); } // Returning null means 'use existing class bytes'. return null; } /** * INTERNAL: * Returns an unqualified class name from the specified class name. * @param name Class name with {@code '/'} as delimiter. * @return Unqualified class name. */ protected static String getShortName(String name) { int pos = name.lastIndexOf('/'); if (pos >= 0) { name = name.substring(pos+1); if (name.endsWith(";")) { name = name.substring(0, name.length()-1); } return name; } return ""; } }