/*
* Copyright (c) OSGi Alliance (2013). All Rights Reserved.
*
* Licensed 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 osgi.jpa.managed.support;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import org.osgi.framework.wiring.BundleWiring;
import osgi.jpa.managed.api.JPABridgePersistenceProvider;
import v2_0.Persistence.PersistenceUnit;
import v2_0.Persistence.PersistenceUnit.Properties.Property;
import v2_0.PersistenceUnitCachingType;
import v2_0.PersistenceUnitValidationModeType;
import aQute.lib.io.IO;
/**
* This class is the interface between the bridge (the manager) and the
* Persistence Provider. It is used by the Persistence Provider to get all
* context information. We create one of these for each persistence unit found
* in a bundle.
*/
class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
private final PersistenceUnit persistenceUnitXml;
private final PersistentBundle sourceBundle;
private final String location;
// TODO can we have more than one transformer? Likely not
private final List<ClassTransformer> transformers = new ArrayList<ClassTransformer>();
private DataSourceWrapper jtadatasource;
private DataSourceWrapper nonjtadatasource;
/**
* Create a new Persistence Unit Info
*
* @param bundle the source bundle
* @param xml The xml of the persistence unit
*/
PersistenceUnitInfoImpl(PersistentBundle bundle, PersistenceUnit xml) throws Exception {
this.sourceBundle = bundle;
this.persistenceUnitXml = xml;
this.location = (String) getProperties().get("location");
}
/**
* Shutdown this persistence unit
*/
void shutdown() {
for (ClassTransformer classTransformer : transformers) {
sourceBundle.bridge.transformersHook.unregister(sourceBundle.bundle, classTransformer);
}
}
/**
* Add a new transformer. SPEC: can this be called multiple times?
*
* @see javax.persistence.spi.PersistenceUnitInfo#addTransformer(javax.persistence.spi.ClassTransformer)
*/
@Override
public void addTransformer(ClassTransformer transformer) {
try {
sourceBundle.bridge.transformersHook.register(sourceBundle.bundle, transformer);
if (transformer != null)
transformers.add(transformer);
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#excludeUnlistedClasses()
*/
@Override
public boolean excludeUnlistedClasses() {
Boolean b = persistenceUnitXml.isExcludeUnlistedClasses();
return b == null || b.booleanValue();
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getClassLoader()
*/
@Override
public synchronized ClassLoader getClassLoader() {
sourceBundle.bridge.log.step("classloader " + getPersistenceUnitName());
if (sourceBundle.bridge.persistenceProvider instanceof JPABridgePersistenceProvider) {
ClassLoader cl = ((JPABridgePersistenceProvider) sourceBundle.bridge.persistenceProvider)
.getClassLoader(sourceBundle.bundle);
if (cl != null)
return cl;
}
return sourceBundle.bundle.adapt(BundleWiring.class).getClassLoader();
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getJarFileUrls()
*/
@Override
public List<URL> getJarFileUrls() {
try {
List<URL> urls = new ArrayList<URL>();
for (String url : persistenceUnitXml.getJarFile()) {
urls.add(new URL(url));
}
return urls;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* We hand out a proxy that automatically enlists any connections on the
* current transaction.
*
* @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
*/
@Override
public synchronized DataSource getJtaDataSource() {
System.out.println("get jta data source " + getPersistenceUnitName());
try {
if (jtadatasource == null) {
jtadatasource = new DataSourceWrapper(sourceBundle.bridge.transactionManager,
sourceBundle.bridge.xaDataSource, true, sourceBundle.bridge.log);
}
return jtadatasource.getDataSource();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getManagedClassNames()
*/
@Override
public List<String> getManagedClassNames() {
return persistenceUnitXml.getClazz();
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getMappingFileNames()
*/
@Override
public List<String> getMappingFileNames() {
return persistenceUnitXml.getMappingFile();
}
/**
* In this method we just create a simple temporary class loader. This class
* loader uses the bundle's class loader as parent but defines the classes
* in this class loader. This has the implicit assumption that the temp
* class loader is used BEFORE any bundle's classes are loaded since a class
* loader does parent delegation first. Sigh, guess it works most of the
* time. There is however, no good alternative though in OSGi we could
* actually refresh the bundle. TODO solve this ordering problem
*
* @see javax.persistence.spi.PersistenceUnitInfo#getNewTempClassLoader()
*/
@Override
public ClassLoader getNewTempClassLoader() {
return new ClassLoader(getClassLoader()) {
//
// Use the bunde's resource interface to get the
// bytes of the classes and define them in this
// loader. Yuck.
//
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
//
// Use path of class, then get the resource
//
String path = className.replace('.', '/').concat(".class");
URL resource = getParent().getResource(path);
if (resource == null)
throw new ClassNotFoundException(className + " as resource " + path + " in " + getParent());
try {
//
// Collect the resource's data and define
// the class
// TODO wonder how this works with security since
// we have no protection domain?
//
ByteArrayOutputStream bout = new ByteArrayOutputStream();
IO.copy(resource.openStream(), bout);
byte[] buffer = bout.toByteArray();
return defineClass(className, buffer, 0, buffer.length);
} catch (Exception e) {
throw new ClassNotFoundException(className + " as resource" + path + " in " + getParent(), e);
}
}
//
// Look for resources in our bundle
//
@Override
protected URL findResource(String resource) {
return getParent().getResource(resource);
}
@Override
protected Enumeration<URL> findResources(String resource) throws IOException {
return getParent().getResources(resource);
}
};
}
/**
* This Data Source is based on a XA Data Source but will not be enlisted in
* a transaction.
*
* @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
*/
@Override
public DataSource getNonJtaDataSource() {
try {
if (nonjtadatasource == null) {
nonjtadatasource = new DataSourceWrapper(sourceBundle.bridge.transactionManager,
sourceBundle.bridge.xaDataSource, false, sourceBundle.bridge.log);
}
return nonjtadatasource.getDataSource();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*
* @see
* javax.persistence.spi.PersistenceUnitInfo#getPersistenceProviderClassName
* ()
*/
@Override
public String getPersistenceProviderClassName() {
PersistenceProvider pp = sourceBundle.bridge.persistenceProvider;
if (pp instanceof JPABridgePersistenceProvider) {
String className = ((JPABridgePersistenceProvider) pp).getPersistentProviderClassName();
if (className != null)
return className;
}
return sourceBundle.bridge.persistenceProvider.getClass().getName();
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getPersistenceUnitName()
*/
@Override
public String getPersistenceUnitName() {
return persistenceUnitXml.getName();
}
/*
* @see
* javax.persistence.spi.PersistenceUnitInfo#getPersistenceUnitRootUrl()
*/
@Override
public URL getPersistenceUnitRootUrl() {
//
// Check if we have an override
//
PersistenceProvider pp = sourceBundle.bridge.persistenceProvider;
if (pp instanceof JPABridgePersistenceProvider) {
URL rootUrl = ((JPABridgePersistenceProvider) pp).getRootUrl(sourceBundle.bundle, location);
return rootUrl;
}
//
// Make one that is OSGi based
//
String loc = location;
int n = loc.lastIndexOf('/');
if (n > 0) {
loc = loc.substring(0, n);
}
if (loc.isEmpty())
loc = "/";
return sourceBundle.bundle.getResource(loc);
}
/*
* TODO handle also version 2.1. This btw seems to have the same schema
* exact for the version?
*
* @see
* javax.persistence.spi.PersistenceUnitInfo#getPersistenceXMLSchemaVersion
* ()
*/
@Override
public String getPersistenceXMLSchemaVersion() {
return "2.0";
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getProperties()
*/
@Override
public Properties getProperties() {
Properties properties = new Properties();
if (persistenceUnitXml.getProperties() != null && persistenceUnitXml.getProperties().getProperty() != null)
for (Property p : persistenceUnitXml.getProperties().getProperty()) {
properties.put(p.getName(), p.getValue());
}
properties.put("hibernate.transaction.manager_lookup_class", "xyz");
return properties;
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode()
*/
@Override
public SharedCacheMode getSharedCacheMode() {
PersistenceUnitCachingType sharedCacheMode = persistenceUnitXml.getSharedCacheMode();
if (sharedCacheMode == null)
return null;
return SharedCacheMode.valueOf(sharedCacheMode.name());
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getTransactionType()
*/
@Override
public PersistenceUnitTransactionType getTransactionType() {
return PersistenceUnitTransactionType.JTA;
}
/*
* @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode()
*/
@Override
public ValidationMode getValidationMode() {
PersistenceUnitValidationModeType validationMode = persistenceUnitXml.getValidationMode();
if (validationMode == null)
return null;
return ValidationMode.valueOf(validationMode.name());
}
}