blob: dddeb91163ff9d76da6622e7d2a6c586feb5a2f0 [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 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.aries.ejb.openejb.extender;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.naming.NamingException;
import org.apache.aries.util.AriesFrameworkUtil;
import org.apache.aries.util.tracker.RecursiveBundleTracker;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.EjbJarInfo;
import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
import org.apache.openejb.assembler.classic.PersistenceContextReferenceInfo;
import org.apache.openejb.assembler.classic.PersistenceUnitReferenceInfo;
import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
import org.apache.openejb.assembler.classic.ReferenceLocationInfo;
import org.apache.openejb.assembler.classic.SecurityServiceInfo;
import org.apache.openejb.assembler.classic.TransactionServiceInfo;
import org.apache.openejb.assembler.dynamic.PassthroughFactory;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.EjbModule;
import org.apache.openejb.config.ValidationContext;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.persistence.JtaEntityManagerRegistry;
import org.apache.openejb.ri.sp.PseudoSecurityService;
import org.apache.openejb.util.OpenEjbVersion;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.util.tracker.BundleTrackerCustomizer;
public class EJBExtender implements BundleActivator, BundleTrackerCustomizer {
private static final int STARTABLE = Bundle.STARTING | Bundle.ACTIVE;
private static final Object PROCESSING_OBJECT = new Object();
private static final Object REMOVING_OBJECT = new Object();
private RecursiveBundleTracker tracker;
private final ConcurrentMap<Bundle, RunningApplication> runningApps =
new ConcurrentHashMap<Bundle, RunningApplication>();
private final ConcurrentMap<Bundle, Object> processingMap =
new ConcurrentHashMap<Bundle, Object>();
public void start(BundleContext context) throws Exception {
//Internal setup
OSGiTransactionManager.init(context);
AriesProxyService.init(context);
try {
AriesPersistenceContextIntegration.init(context);
} catch (NoClassDefFoundError ncdfe) {
//TODO log that no JPA Context integration is available
}
//Setup OpenEJB with our own extensions
setupOpenEJB();
tracker = new RecursiveBundleTracker(context, Bundle.INSTALLED | Bundle.RESOLVED |
Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING, this);
tracker.open();
}
private void setupOpenEJB() throws OpenEJBException {
//Avoid a ClassLoader problem
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(OpenEjbVersion.class.getClassLoader());
OpenEjbVersion.get();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
Assembler a = new Assembler();
TransactionServiceInfo tsi = new TransactionServiceInfo();
tsi.service = "TransactionManager";
tsi.id = "OSGi Transaction Manager";
PassthroughFactory.add(tsi, OSGiTransactionManager.get());
//Avoid another ClassLoader problem
try {
Thread.currentThread().setContextClassLoader(PassthroughFactory.class.getClassLoader());
a.createTransactionManager(tsi);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
try {
//Overwrite existing, default JPA integration with an Aries JPA integrated one
Assembler.getContext().put(JtaEntityManagerRegistry.class.getName(),
AriesPersistenceContextIntegration.get());
SystemInstance.get().setComponent(JtaEntityManagerRegistry.class,
AriesPersistenceContextIntegration.get());
} catch (NoClassDefFoundError ncdfe) {
//TODO log that no JPA Context integration is available
}
SecurityServiceInfo ssi = new SecurityServiceInfo();
ssi.service = "SecurityService";
ssi.id = "Pseudo Security Service";
PassthroughFactory.add(ssi, new PseudoSecurityService());
//Avoid another ClassLoader problem
try {
Thread.currentThread().setContextClassLoader(PassthroughFactory.class.getClassLoader());
a.createSecurityService(ssi);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
ProxyFactoryInfo proxyFactoryInfo = new ProxyFactoryInfo();
proxyFactoryInfo.id = "Aries ProxyFactory";
proxyFactoryInfo.service = "ProxyFactory";
proxyFactoryInfo.properties = new Properties();
PassthroughFactory.add(proxyFactoryInfo, AriesProxyService.get());
try {
Thread.currentThread().setContextClassLoader(PassthroughFactory.class.getClassLoader());
a.createProxyFactory(proxyFactoryInfo);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public void stop(BundleContext context) throws Exception {
tracker.close();
AriesProxyService.get().destroy();
OSGiTransactionManager.get().destroy();
try {
AriesPersistenceContextIntegration.get().destroy();
} catch (NoClassDefFoundError ncdfe) {
//TODO log that no JPA Context integration is available
}
}
public Object addingBundle(Bundle bundle, BundleEvent event) {
if(mightContainEJBs(bundle)) {
if((bundle.getState() & STARTABLE) != 0) {
startEJBs(bundle);
}
return bundle;
}
return null;
}
private boolean mightContainEJBs(Bundle bundle) {
Dictionary<String, String> headers = bundle.getHeaders();
return (headers.get("Export-EJB") != null) || (headers.get("Web-ContextPath") != null);
}
public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
if((bundle.getState() & STARTABLE) != 0) {
startEJBs(bundle);
} else if (bundle.getState() == Bundle.STOPPING) {
stopEJBs(bundle);
}
}
private void startEJBs(final Bundle bundle) {
try {
//If there is another thread adding or removing then stop here
Object o = processingMap.put(bundle, PROCESSING_OBJECT);
if(o == REMOVING_OBJECT || o == PROCESSING_OBJECT) {
return;
}
//If already running then avoid
if(runningApps.get(bundle) != null)
return;
//Broken validation for persistence :(
EjbModule ejbModule = new EjbModule(AriesFrameworkUtil.getClassLoaderForced(bundle), null, null, null);
try {
Field f = EjbModule.class.getDeclaredField("validation");
f.setAccessible(true);
f.set(ejbModule, new ValidationProofValidationContext(ejbModule));
} catch (Exception e) {
// Hmmm
}
addAltDDs(ejbModule, bundle);
//We build our own because we can't trust anyone to get the classpath right otherwise!
ejbModule.setFinder(new OSGiFinder(bundle));
ConfigurationFactory configurationFactory = new ConfigurationFactory();
EjbJarInfo ejbInfo = null;
//Avoid yet another ClassLoading problem
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(new ClassLoader(OpenEjbVersion.class.getClassLoader()) {
protected Class<?> findClass(String name) throws ClassNotFoundException {
for(Bundle b : bundle.getBundleContext().getBundles()) {
if(b.getSymbolicName().contains("jaxb-impl"))
return b.loadClass(name);
}
throw new ClassNotFoundException(name);
}
});
ejbInfo = configurationFactory.configureApplication(ejbModule);
//Another oddity here
ejbInfo.validationInfo = null;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
processJPAMappings(ejbInfo);
Assembler assembler = (Assembler) SystemInstance.get().getComponent(Assembler.class);
RunningApplication app = null;
try {
SystemInstance.get().setProperty("openejb.geronimo", "true");
cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(OpenEjbVersion.class.getClassLoader());
app = new RunningApplication(assembler.createApplication(ejbInfo,
new AppClassLoader(ejbModule.getClassLoader())), bundle, ejbInfo.enterpriseBeans);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
} finally {
SystemInstance.get().getProperties().remove("openejb.geronimo");
}
runningApps.put(bundle, app);
app.init();
} catch (OpenEJBException oee) {
// TODO Auto-generated catch block
oee.printStackTrace();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(processingMap.remove(bundle) == REMOVING_OBJECT) {
stopEJBs(bundle);
}
}
}
private void processJPAMappings(EjbJarInfo ejbInfo) {
for(EnterpriseBeanInfo ebi : ejbInfo.enterpriseBeans){
for(PersistenceUnitReferenceInfo pui : ebi.jndiEnc.persistenceUnitRefs) {
pui.location = new ReferenceLocationInfo();
pui.location.jndiName = "aries/integration/unit/" + pui.persistenceUnitName;
}
for(PersistenceContextReferenceInfo pci : ebi.jndiEnc.persistenceContextRefs) {
pci.location = new ReferenceLocationInfo();
pci.location.jndiName = "aries/integration/context/" + pci.persistenceUnitName;
}
}
}
private void addAltDDs(EjbModule ejbModule, Bundle bundle) {
Map<String, Object> altDDs = ejbModule.getAltDDs();
String folder = (bundle.getHeaders().get("Web-ContextPath") == null) ?
"META-INF" : "WEB-INF";
Enumeration<URL> e = bundle.findEntries(folder, "*.xml", false);
if(e == null)
return;
for(URL u : Collections.list(e)) {
String urlString = u.toExternalForm();
urlString = urlString.substring(urlString.lastIndexOf('/') + 1);
altDDs.put(urlString, u);
}
//Persistence descriptors are handled by Aries JPA, but OpenEJB fails validation
//if we hide them. As a result we switch it off.
//altDDs.remove("persistence.xml");
}
private void stopEJBs(Bundle bundle) {
if(processingMap.put(bundle, REMOVING_OBJECT) == PROCESSING_OBJECT)
return;
else {
try {
RunningApplication app = runningApps.remove(bundle);
if(app != null) {
app.destroy();
Assembler assembler = (Assembler) SystemInstance.get().getComponent(Assembler.class);
assembler.destroyApplication(app.getCtx());
}
} catch (OpenEJBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(processingMap.remove(bundle) == PROCESSING_OBJECT)
startEJBs(bundle);
}
}
}
public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
if (bundle.getState() == Bundle.STOPPING) {
stopEJBs(bundle);
}
}
private static final class ValidationProofValidationContext extends ValidationContext {
private ValidationProofValidationContext(EjbModule mod) {
super(mod);
}
@Override
public boolean hasErrors() {
return false;
}
@Override
public boolean hasFailures() {
return false;
}
@Override
public boolean hasWarnings() {
return false;
}
}
private static final class AppClassLoader extends ClassLoader {
private AppClassLoader(ClassLoader parentLoader) {
super(parentLoader);
}
@Override
protected Class<?> findClass(String className)
throws ClassNotFoundException {
return Class.forName(className, false, OpenEjbVersion.class.getClassLoader());
}
}
}