blob: 9992e2c9a5aa7690a067b5551261bc5f720e9828 [file] [log] [blame]
/*
* 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 WARRANTIESOR 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.aries.jpa.container.weaving.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import javax.persistence.spi.ClassTransformer;
import org.osgi.framework.Bundle;
import org.osgi.framework.hooks.weaving.WeavingException;
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.hooks.weaving.WovenClass;
import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This weaving hook delegates to any registered {@link ClassTransformer} instances for a given bundle
*/
public class JPAWeavingHook implements WeavingHook, TransformerRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger(JPAWeavingHook.class);
/**
* This constructor should not be called directly, the {@link JPAWeavingHookFactory} should be used to
* ensure that Weaving support is available.
*/
JPAWeavingHook() {
}
/**
* With luck we will only have one persistence unit per bundle, but if we don't we'll need to call them
* until one of them does a transform or we run out.
*/
private final Map<Bundle, LinkedHashSet<ClassTransformer>> registeredTransformers = new HashMap<Bundle, LinkedHashSet<ClassTransformer>>();
public void weave(WovenClass wovenClass) {
BundleWiring wiring = wovenClass.getBundleWiring();
Bundle bundle = wiring.getBundle();
ClassLoader cl = wiring.getClassLoader();
Collection<ClassTransformer> transformersToTry = getTransformers(bundle);
if (transformersToTry.size() == 0 && wovenClass.getClassName().endsWith("Car")) {
LOGGER.error("Loading " + wovenClass.getClassName() + " before transformer is present");
//for (StackTraceElement el : Thread.currentThread().getStackTrace()) {
// LOGGER.info(el.toString());
// }
}
for (ClassTransformer transformer : transformersToTry) {
if (transformClass(wovenClass, cl, transformer)) {
LOGGER.info("Weaving " + wovenClass.getClassName() + " using " + transformer.getClass().getName());
break;
};
}
}
@SuppressWarnings("unchecked")
private synchronized Collection<ClassTransformer> getTransformers(Bundle bundle) {
LinkedHashSet<ClassTransformer> transformers = registeredTransformers.get(bundle);
return transformers != null ? new ArrayList<ClassTransformer>(transformers) : Collections.EMPTY_LIST;
}
private boolean transformClass(WovenClass wovenClass, ClassLoader cl, ClassTransformer transformer)
throws ThreadDeath, OutOfMemoryError {
try {
byte[] result = transformer
.transform(cl,
wovenClass.getClassName(),
wovenClass.getDefinedClass(),
wovenClass.getProtectionDomain(),
wovenClass.getBytes());
if (result != null) {
wovenClass.setBytes(result);
wovenClass.getDynamicImports().add("org.eclipse.persistence.*");
wovenClass.getDynamicImports().add("org.apache.openjpa.*");
return true;
}
} catch (Throwable t) {
if (t instanceof ThreadDeath)
throw (ThreadDeath)t;
else if (t instanceof OutOfMemoryError)
throw (OutOfMemoryError)t;
else {
Bundle b = wovenClass.getBundleWiring().getBundle();
String msg = String.format("Weaving failure", wovenClass.getClassName(),
b.getSymbolicName(), b.getVersion(), transformer);
throw new WeavingException(msg, t);
}
}
return false;
}
public synchronized void addTransformer(Bundle pBundle, ClassTransformer transformer) {
LOGGER.info("Adding transformer " + transformer.getClass().getName());
LinkedHashSet<ClassTransformer> transformers = registeredTransformers.get(pBundle);
if (transformers == null) {
transformers = new LinkedHashSet<ClassTransformer>();
registeredTransformers.put(pBundle, transformers);
}
transformers.add(transformer);
}
public synchronized void removeTransformer(Bundle pBundle, ClassTransformer transformer) {
LinkedHashSet<ClassTransformer> set = registeredTransformers.get(pBundle);
if (set == null || !set.remove(transformer)) {
throw new IllegalStateException("Transformer " + transformer + " not registered");
}
if (set.isEmpty()) {
registeredTransformers.remove(pBundle);
}
}
}