blob: 289683106eb2eb985b3de5254774f10de4b9bed2 [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.openejb.assembler.classic;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.apache.openejb.core.cmp.cmp2.Cmp2Generator;
import org.apache.openejb.core.cmp.cmp2.CmrField;
import org.apache.openejb.core.cmp.cmp2.Cmp1Generator;
import org.apache.openejb.core.cmp.CmpUtil;
import org.apache.openejb.ClassLoaderUtil;
import org.apache.openejb.util.UrlCache;
/**
* Creates a jar file which contains the CMP implementation classes and the cmp entity mappings xml file.
*/
public class CmpJarBuilder {
private final ClassLoader tempClassLoader;
private File jarFile;
private final Set<String> entries = new TreeSet<String>();
private final AppInfo appInfo;
public CmpJarBuilder(AppInfo appInfo, ClassLoader classLoader) {
this.appInfo = appInfo;
tempClassLoader = ClassLoaderUtil.createTempClassLoader(classLoader);
}
public File getJarFile() throws IOException {
if (jarFile == null) {
generate();
}
return jarFile;
}
/**
* Generate the CMP jar file associated with this
* deployed application. The generated jar file will
* contain generated classes and metadata that will
* allow the JPA engine to manage the bean persistence.
*
* @exception IOException
*/
private void generate() throws IOException {
// Don't generate an empty jar. If there are no container-managed beans defined in this
// application deployment, there's nothing to do.
if (!hasCmpBeans()) {
return;
}
boolean threwException = false;
JarOutputStream jarOutputStream = openJarFile();
try {
// Generate CMP implementation classes
for (EjbJarInfo ejbJar : appInfo.ejbJars) {
for (EnterpriseBeanInfo beanInfo : ejbJar.enterpriseBeans) {
if (beanInfo instanceof EntityBeanInfo) {
EntityBeanInfo entityBeanInfo = (EntityBeanInfo) beanInfo;
if ("CONTAINER".equalsIgnoreCase(entityBeanInfo.persistenceType)) {
generateClass(jarOutputStream, entityBeanInfo);
}
}
}
}
if (appInfo.cmpMappingsXml != null) {
// System.out.println(appInfo.cmpMappingsXml);
addJarEntry(jarOutputStream, "META-INF/openejb-cmp-generated-orm.xml", appInfo.cmpMappingsXml.getBytes());
}
} catch (IOException e) {
threwException = true;
throw e;
} finally {
close(jarOutputStream);
if (threwException) {
jarFile.delete();
jarFile = null;
}
}
}
/**
* Test if an application contains and CMP beans that
* need to be mapped to the JPA persistence engine. This
* will search all of the ejb jars contained within
* the application looking for Entity beans with
* a CONTAINER persistence type.
*
* @return true if the application uses container managed beans,
* false if none are found.
*/
private boolean hasCmpBeans() {
for (EjbJarInfo ejbJar : appInfo.ejbJars) {
for (EnterpriseBeanInfo beanInfo : ejbJar.enterpriseBeans) {
if (beanInfo instanceof EntityBeanInfo) {
EntityBeanInfo entityBeanInfo = (EntityBeanInfo) beanInfo;
if ("CONTAINER".equalsIgnoreCase(entityBeanInfo.persistenceType)) {
return true;
}
}
}
}
return false;
}
/**
* Generate a class file for a CMP bean, writing the
* byte data for the generated class into the jar file
* we're constructing.
*
* @param jarOutputStream
* The target jarfile.
* @param entityBeanInfo
* The descriptor for the entity bean we need to wrapper.
*
* @exception IOException
*/
private void generateClass(JarOutputStream jarOutputStream, EntityBeanInfo entityBeanInfo) throws IOException {
// don't generate if there is aleady an implementation class
String cmpImplClass = CmpUtil.getCmpImplClassName(entityBeanInfo.abstractSchemaName, entityBeanInfo.ejbClass);
String entryName = cmpImplClass.replace(".", "/") + ".class";
if (entries.contains(entryName) || tempClassLoader.getResource(entryName) != null) {
return;
}
// load the bean class, which is used by the generator
Class<?> beanClass = null;
try {
beanClass = tempClassLoader.loadClass(entityBeanInfo.ejbClass);
} catch (ClassNotFoundException e) {
throw (IOException)new IOException("Could not find entity bean class " + beanClass).initCause(e);
}
// and the primary key class, if defined.
Class<?> primKeyClass = null;
if (entityBeanInfo.primKeyClass != null) {
try {
primKeyClass = tempClassLoader.loadClass(entityBeanInfo.primKeyClass);
} catch (ClassNotFoundException e) {
throw (IOException)new IOException("Could not find entity primary key class " + entityBeanInfo.primKeyClass).initCause(e);
}
}
// now generate a class file using the appropriate level of CMP generator.
byte[] bytes;
// NB: We'll need to change this test of CMP 3 is ever defined!
if (entityBeanInfo.cmpVersion != 2) {
Cmp1Generator cmp1Generator = new Cmp1Generator(cmpImplClass, beanClass);
// A primary key class defined as Object is an unknown key. Mark it that
// way so the generator will create the automatically generated key.
if ("java.lang.Object".equals(entityBeanInfo.primKeyClass)) {
cmp1Generator.setUnknownPk(true);
}
bytes = cmp1Generator.generate();
} else {
// generate the implementation class
Cmp2Generator cmp2Generator = new Cmp2Generator(cmpImplClass,
beanClass,
entityBeanInfo.primKeyField,
primKeyClass,
entityBeanInfo.cmpFieldNames.toArray(new String[entityBeanInfo.cmpFieldNames.size()]));
// we need to have a complete set of the defined CMR fields available for the
// generation process as well.
for (CmrFieldInfo cmrFieldInfo : entityBeanInfo.cmrFields) {
EntityBeanInfo roleSource = cmrFieldInfo.mappedBy.roleSource;
CmrField cmrField = new CmrField(cmrFieldInfo.fieldName,
cmrFieldInfo.fieldType,
CmpUtil.getCmpImplClassName(roleSource.abstractSchemaName, roleSource.ejbClass),
roleSource.local,
cmrFieldInfo.mappedBy.fieldName,
cmrFieldInfo.synthetic);
cmp2Generator.addCmrField(cmrField);
}
bytes = cmp2Generator.generate();
}
// add the generated class to the jar
addJarEntry(jarOutputStream, entryName, bytes);
}
/**
* Insert a file resource into the generated jar file.
*
* @param jarOutputStream
* The target jar file.
* @param fileName The name we're inserting.
* @param bytes The file byte data.
*
* @exception IOException
*/
private void addJarEntry(JarOutputStream jarOutputStream, String fileName, byte[] bytes) throws IOException {
// add all missing directory entried
String path = "";
for (StringTokenizer tokenizer = new StringTokenizer(fileName, "/"); tokenizer.hasMoreTokens();) {
String part = tokenizer.nextToken();
if (tokenizer.hasMoreTokens()) {
path += part + "/";
if (!entries.contains(path)) {
jarOutputStream.putNextEntry(new JarEntry(path));
jarOutputStream.closeEntry();
entries.add(path);
}
}
}
// write the bytes
jarOutputStream.putNextEntry(new JarEntry(fileName));
try {
jarOutputStream.write(bytes);
} finally {
jarOutputStream.closeEntry();
entries.add(fileName);
}
}
private JarOutputStream openJarFile() throws IOException {
if (jarFile != null) {
throw new IllegalStateException("Jar file is closed");
}
// if url caching is enabled, generate the file directly in the cache dir, so it doesn't have to be recoppied
if (UrlCache.cacheDir != null) {
jarFile = File.createTempFile("OpenEJB_Generated_", ".jar", UrlCache.cacheDir);
} else {
jarFile = File.createTempFile("OpenEJB_Generated_", ".jar");
}
jarFile.deleteOnExit();
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile));
return jarOutputStream;
}
private void close(JarOutputStream jarOutputStream) {
if (jarOutputStream != null) {
try {
jarOutputStream.close();
} catch (IOException ignored) {
}
}
}
}