All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.util.weave.j2se6.DCRWeaver Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2009 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.util.weave.j2se6;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import net.grinder.util.Pair;
import net.grinder.util.weave.Weaver;
import net.grinder.util.weave.WeavingException;


/**
 * {@link Weaver} that uses Java 6 dynamic class retransformation.
 *
 * @author Philip Aston
 */
public final class DCRWeaver implements Weaver {

  // Guarded by this.
  private final Set> m_pendingClasses = new HashSet>();

  private final PointCutRegistryImplementation m_pointCutRegistry =
    new PointCutRegistryImplementation();
  private final ClassFileTransformer m_transformer;

  private final Instrumentation m_instrumentation;

  /**
   * Constructor.
   *
   * @param transformerFactory Used to create the transformer.
   * @param instrumentation Access to the JVM instrumentation.
   */
  public DCRWeaver(ClassFileTransformerFactory transformerFactory,
                   Instrumentation instrumentation) {

    m_instrumentation = instrumentation;

    m_transformer = transformerFactory.create(m_pointCutRegistry);

    m_instrumentation.addTransformer(m_transformer, true);
  }

  /**
   * {@inheritDoc}
   */
  @Override public String weave(Constructor constructor) {
    return m_pointCutRegistry.add(constructor);
  }

  /**
   * {@inheritDoc}
   */
  @Override public String weave(Method method, TargetSource targetSource)
    throws WeavingException {
    if (!targetSource.canApply(method)) {
      throw new WeavingException("Insufficient parameters for " +
                                 targetSource + ": " + method.toString());
    }

    return m_pointCutRegistry.add(method, targetSource);
  }

  /**
   * {@inheritDoc}
   */
  @Override public void applyChanges() throws WeavingException {
    synchronized (this) {
      if (m_pendingClasses.size() > 0) {
        try {
          m_instrumentation.retransformClasses(
            m_pendingClasses.toArray(new Class[0]));
        }
        catch (UnmodifiableClassException e) {
          throw new WeavingException("Failed to modify class", e);
        }

        m_pendingClasses.clear();
      }
    }
  }

  private static final String s_classLoaderIdentity =
    Integer.toHexString(DCRWeaver.class.hashCode()) + ":";
  private static final AtomicLong s_nextLocation = new AtomicLong();

  private static String generateLocationString() {
    return s_classLoaderIdentity + s_nextLocation.getAndIncrement();
  }


  /**
   * Factory that generates {@link ClassFileTransformer}s which perform
   * the weaving.
   */
  public interface ClassFileTransformerFactory {

    /**
     * Factory method.
     *
     * @param pointCutRegistry The point cut registry.
     * @return The transformer.
     */
    ClassFileTransformer create(PointCutRegistry pointCutRegistry);
  }

  private final class PointCutRegistryImplementation
    implements PointCutRegistry {
    // Guarded by this.
    private final Map, String> m_wovenMembers =
      new HashMap, String>();

    // Pre-calculated mapping of internal class name -> constructor -> weaving
    // details, for efficiency.
    // Guarded by this.
    private final Map, List>>
      m_internalClassNameToConstructorToLocation =
        new HashMap, List>>();

    // Pre-calculated mapping of internal class name -> method -> weaving
    // details, for efficiency.
    // Guarded by this.
    private final Map>>
      m_internalClassNameToMethodToLocation =
        new HashMap>>();

    public Map, List>
      getConstructorPointCutsForClass(String className) {
        return m_internalClassNameToConstructorToLocation.get(className);
    }

    public Map>
      getMethodPointCutsForClass(String className) {
        return m_internalClassNameToMethodToLocation.get(className);
    }

    public String add(Constructor constructor) {
      return add(constructor,
                 TargetSource.CLASS,
                 m_internalClassNameToConstructorToLocation);
    }

    public String add(Method method, TargetSource targetSource) {
      return add(method, targetSource, m_internalClassNameToMethodToLocation);
    }

    private  String add(
      T member,
      TargetSource targetSource,
      Map>> classNameToMemberToLocation) {

      final Pair locationKey =
          Pair.of((Member) member, targetSource);

      synchronized (this) {
        final String alreadyWoven = m_wovenMembers.get(locationKey);

        if (alreadyWoven != null) {
          return alreadyWoven;
        }
      }

      final String className = member.getDeclaringClass().getName();
      final String internalClassName = className.replace('.', '/');
      final String location = generateLocationString();

      synchronized (this) {
        final Map> memberToWeavingDetails;

        final Map> existingMap =
          classNameToMemberToLocation.get(internalClassName);

        if (existingMap != null) {
          memberToWeavingDetails = existingMap;
        }
        else {
          memberToWeavingDetails = new HashMap>();
          classNameToMemberToLocation.put(internalClassName,
                                          memberToWeavingDetails);
        }

        m_wovenMembers.put(locationKey, location);

        final List weavingDetailsList;

        final List existingList =
          memberToWeavingDetails.get(member);

        if (existingList != null) {
          weavingDetailsList = existingList;
        }
        else {
          weavingDetailsList = new ArrayList();
          memberToWeavingDetails.put(member, weavingDetailsList);
        }

        weavingDetailsList.add(new WeavingDetails(location, targetSource));
      }

      synchronized (DCRWeaver.this) {
        m_pendingClasses.add(member.getDeclaringClass());
      }

      return location;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy