/* * 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.tools.ant.taskdefs.optional.ejb; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; import java.util.Hashtable; import java.util.List; import javax.xml.parsers.SAXParser; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Java; import org.apache.tools.ant.types.Path; /** * The deployment tool to add the jonas specific deployment descriptors to the * ejb JAR file. JONAS only requires one additional file jonas-ejb-jar.xml. * * @version 1.0 * @see EjbJar#createJonas */ public class JonasDeploymentTool extends GenericDeploymentTool { /** Public Id of the standard deployment descriptor DTD. */ protected static final String EJB_JAR_1_1_PUBLIC_ID = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; protected static final String EJB_JAR_2_0_PUBLIC_ID = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"; /** Public Id of the JOnAS-specific deployment descriptor DTD. */ protected static final String JONAS_EJB_JAR_2_4_PUBLIC_ID = "-//ObjectWeb//DTD JOnAS 2.4//EN"; protected static final String JONAS_EJB_JAR_2_5_PUBLIC_ID = "-//ObjectWeb//DTD JOnAS 2.5//EN"; /** RMI ORB. */ protected static final String RMI_ORB = "RMI"; /** JEREMIE ORB. */ protected static final String JEREMIE_ORB = "JEREMIE"; /** DAVID ORB. */ protected static final String DAVID_ORB = "DAVID"; /** * Name of the standard deployment descriptor DTD (these files are stored in * the ${JONAS_ROOT}/xml directory). */ protected static final String EJB_JAR_1_1_DTD = "ejb-jar_1_1.dtd"; protected static final String EJB_JAR_2_0_DTD = "ejb-jar_2_0.dtd"; /** * Name of the JOnAS-specific deployment descriptor DTD (these files are * stored in the ${JONAS_ROOT}/xml directory). */ protected static final String JONAS_EJB_JAR_2_4_DTD = "jonas-ejb-jar_2_4.dtd"; protected static final String JONAS_EJB_JAR_2_5_DTD = "jonas-ejb-jar_2_5.dtd"; /** Default JOnAS deployment descriptor name. */ protected static final String JONAS_DD = "jonas-ejb-jar.xml"; /** GenIC class name (JOnAS 2.5) */ protected static final String GENIC_CLASS = "org.objectweb.jonas_ejb.genic.GenIC"; /** Old GenIC class name (JOnAS 2.4.x). */ protected static final String OLD_GENIC_CLASS_1 = "org.objectweb.jonas_ejb.tools.GenWholeIC"; /** Old GenIC class name. */ protected static final String OLD_GENIC_CLASS_2 = "org.objectweb.jonas_ejb.tools.GenIC"; /** * Filename of the standard EJB descriptor (which is passed to this class * from the parent "ejbjar" task). This file is relative to the directory * specified by the "srcdir" attribute in the ejbjar task. */ private String descriptorName; /** * Filename of the JOnAS-specific EJB descriptor (which is passed to this * class from the parent "ejbjar" task). This file is relative to the * directory specified by the "srcdir" attribute in the ejbjar task. */ private String jonasDescriptorName; /* ------------- */ /* GenIC options */ /* ------------- */ /** * Temporary output directory used by GenIC. */ private File outputdir; /** * <code>true</code> if the intermediate Java source files generated by * GenIC must be deleted or not. The default is <code>false</code> */ private boolean keepgenerated = false; /** * <code>true</code> if the generated source files must not be compiled via * the java and rmi compilers. The default is <code>false</code>. */ private boolean nocompil = false; /** * <code>true</code> if the XML deployment descriptors must be parsed * without validation. The default is <code>false</code>. */ private boolean novalidation = false; /** * Java compiler to use. The default is the value of * <code>build.compiler</code> property. */ private String javac; /** Options to pass to the java compiler. */ private String javacopts; /** Options to pass to the rmi compiler. */ private String rmicopts; /** * Whether or not the RMI skeleton and stub must be modified to * implement the implicit propagation of the security context (the * transactional context is always provided). The default is * <code>false</code>. */ private boolean secpropag = false; /** * <code>true</code> if the GenIC call must be verbose. The default * is <code>false</code>. */ private boolean verbose = false; /** Additional args to send to GenIC. */ private String additionalargs; /* ------------- */ /* other options */ /* ------------- */ /** JOnAS root directory. */ private File jonasroot; /** * <code>true</code> if the generic JAR file used as input to GenIC must be * retained. The default is <code>false</code>. */ private boolean keepgeneric = false; /** Stores the suffix for the JOnAS JAR file. The default is '.jar'. */ private String suffix = ".jar"; /** * ORB to use (RMI, JEREMIE or DAVID). If omitted, it defaults to the one * present in classpath. If specified, the corresponding JOnAS JAR is * automatically added to the classpath. */ private String orb; /** * <code>true</code> if GenIC must not be run on the EJB JAR. * The default is <code>false</code>. */ private boolean nogenic = false; /* -------------------- */ /* GenIC options setter */ /* -------------------- */ /** * Sets the <code>keepgenerated</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setKeepgenerated(boolean aBoolean) { keepgenerated = aBoolean; } /** * Sets the additional arguments. * * @param aString additional args. */ public void setAdditionalargs(String aString) { additionalargs = aString; } /** * Sets the <code>nocompil</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setNocompil(boolean aBoolean) { nocompil = aBoolean; } /** * Sets the <code>novalidation</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setNovalidation(boolean aBoolean) { novalidation = aBoolean; } /** * Sets the java compiler to use. * * @param aString the java compiler. */ public void setJavac(String aString) { javac = aString; } /** * Set the options to pass to the java compiler. * * @param aString the options. */ public void setJavacopts(String aString) { javacopts = aString; } /** * Set the options to pass to the rmi compiler. * * @param aString the options. */ public void setRmicopts(String aString) { rmicopts = aString; } /** * Sets the <code>secpropag</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setSecpropag(boolean aBoolean) { secpropag = aBoolean; } /** * Sets the <code>verbose</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setVerbose(boolean aBoolean) { verbose = aBoolean; } /* -------------------- */ /* other options setter */ /* -------------------- */ /** * Set the JOnAS root directory. * * @param aFile the JOnAS root directory. */ public void setJonasroot(File aFile) { jonasroot = aFile; } /** * Sets the <code>keepgeneric</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setKeepgeneric(boolean aBoolean) { keepgeneric = aBoolean; } /** * Sets the jar suffix. * * @param aString the string to use as the suffix. */ public void setJarsuffix(String aString) { suffix = aString; } /** * Sets the <code>orb</code> to construct classpath. * * @param aString 'RMI', 'JEREMIE', or 'DAVID'. */ public void setOrb(String aString) { orb = aString; } /** * Sets the <code>nogenic</code> flag. * * @param aBoolean <code>true</code> if the flag must be set. */ public void setNogenic(boolean aBoolean) { nogenic = aBoolean; } /* ------------- */ /* other methods */ /* ------------- */ /** {@inheritDoc}. */ @Override public void processDescriptor(String aDescriptorName, SAXParser saxParser) { descriptorName = aDescriptorName; log("JOnAS Deployment Tool processing: " + descriptorName, Project.MSG_VERBOSE); super.processDescriptor(descriptorName, saxParser); if (outputdir != null) { // the method deleteOnExit() do not work because the directory is not empty log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE); deleteAllFiles(outputdir); } } /** {@inheritDoc}. */ @Override protected void writeJar(String baseName, File jarfile, Hashtable<String, File> ejbFiles, String publicId) throws BuildException { // create the generic jar first File genericJarFile = super.getVendorOutputJarFile(baseName); super.writeJar(baseName, genericJarFile, ejbFiles, publicId); // GenIC call on generic jar addGenICGeneratedFiles(genericJarFile, ejbFiles); // create the real jar super.writeJar(baseName, getVendorOutputJarFile(baseName), ejbFiles, publicId); if (!keepgeneric) { log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE); genericJarFile.delete(); } } /** {@inheritDoc}. */ @Override protected void addVendorFiles(Hashtable<String, File> ejbFiles, String ddPrefix) { // JOnAS-specific descriptor deployment jonasDescriptorName = getJonasDescriptorName(); File jonasDD = new File(getConfig().descriptorDir, jonasDescriptorName); if (jonasDD.exists()) { ejbFiles.put(META_DIR + JONAS_DD, jonasDD); } else { log("Unable to locate the JOnAS deployment descriptor. It was expected to be in: " + jonasDD.getPath() + ".", Project.MSG_WARN); } } /** {@inheritDoc}. */ @Override protected File getVendorOutputJarFile(String baseName) { return new File(getDestDir(), baseName + suffix); } /** * Determines the name of the JOnAS-specific EJB descriptor using the * specified standard EJB descriptor name. In general, the standard * descriptor will be named "[basename]-ejb-jar.xml", and this method will * return "[basename]-jonas-ejb-jar.xml" or "jonas-[basename].xml" * * @return The name of the JOnAS-specific EJB descriptor file. */ private String getJonasDescriptorName() { // descriptorName = <path><basename><basenameterminator><remainder> // examples = /org/objectweb/fooAppli/foo/Foo-ejb-jar.xml // examples = /org/objectweb/fooAppli/foo/Foo.xml (JOnAS convention) String jonasDN; // JOnAS-specific DD boolean jonasConvention = false; // true if the JOnAS convention is used for the DD String path; // Directory path of the EJB descriptor String fileName; // EJB descriptor file name String baseName; // Filename appearing before name terminator String remainder; // Filename appearing after the name terminator int startOfFileName = descriptorName.lastIndexOf(File.separatorChar); if (startOfFileName != -1) { // extract path info path = descriptorName.substring(0, startOfFileName + 1); fileName = descriptorName.substring(startOfFileName + 1); } else { // descriptorName is just a file without path path = ""; fileName = descriptorName; } if (fileName.startsWith(EJB_DD)) { return path + JONAS_DD; } int endOfBaseName = descriptorName.indexOf(getConfig().baseNameTerminator, startOfFileName); /* * Check for the odd case where the terminator and/or filename * extension aren't found. These will ensure "jonas-" appears at the * end of the name and before the '.' (if present). */ if (endOfBaseName < 0) { // baseNameTerminator not found: the descriptor use the // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and // not [Foo<baseNameTerminator>-ejb-jar.xml, // Foo<baseNameTerminator>-jonas-ejb-jar.xml]. endOfBaseName = descriptorName.lastIndexOf('.') - 1; if (endOfBaseName < 0) { // no . found endOfBaseName = descriptorName.length() - 1; } jonasConvention = true; } baseName = descriptorName.substring(startOfFileName + 1, endOfBaseName + 1); remainder = descriptorName.substring(endOfBaseName + 1); if (jonasConvention) { jonasDN = path + "jonas-" + baseName + ".xml"; } else { jonasDN = path + baseName + "jonas-" + remainder; } log("Standard EJB descriptor name: " + descriptorName, Project.MSG_VERBOSE); log("JOnAS-specific descriptor name: " + jonasDN, Project.MSG_VERBOSE); return jonasDN; } /** {@inheritDoc}. */ @Override protected String getJarBaseName(String descriptorFileName) { String baseName = null; if (getConfig().namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) { // try to find JOnAS specific convention name if (descriptorFileName.indexOf(getConfig().baseNameTerminator) == -1) { // baseNameTerminator not found: the descriptor use the // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and // not [Foo<baseNameTerminator>-ejb-jar.xml, // Foo<baseNameTerminator>-jonas-ejb-jar.xml]. String aCanonicalDescriptor = descriptorFileName.replace('\\', '/'); int lastSeparatorIndex = aCanonicalDescriptor.lastIndexOf('/'); int endOfBaseName; if (lastSeparatorIndex != -1) { endOfBaseName = descriptorFileName.indexOf(".xml", lastSeparatorIndex); } else { endOfBaseName = descriptorFileName.indexOf(".xml"); } if (endOfBaseName != -1) { baseName = descriptorFileName.substring(0, endOfBaseName); } } } if (baseName == null) { // else get standard baseName baseName = super.getJarBaseName(descriptorFileName); } log("JAR base name: " + baseName, Project.MSG_VERBOSE); return baseName; } /** {@inheritDoc}. */ @Override protected void registerKnownDTDs(DescriptorHandler handler) { handler.registerDTD(EJB_JAR_1_1_PUBLIC_ID, jonasroot + File.separator + "xml" + File.separator + EJB_JAR_1_1_DTD); handler.registerDTD(EJB_JAR_2_0_PUBLIC_ID, jonasroot + File.separator + "xml" + File.separator + EJB_JAR_2_0_DTD); handler.registerDTD(JONAS_EJB_JAR_2_4_PUBLIC_ID, jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_4_DTD); handler.registerDTD(JONAS_EJB_JAR_2_5_PUBLIC_ID, jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_5_DTD); } /** * Add to the given hashtable all the file generated by GenIC. * * @param genericJarFile jar file. * @param ejbFiles the hashtable. */ private void addGenICGeneratedFiles( File genericJarFile, Hashtable<String, File> ejbFiles) { if (nogenic) { return; } Java genicTask = new Java(getTask()); // GenIC task genicTask.setTaskName("genic"); genicTask.setFork(true); // jonasroot genicTask.createJvmarg().setValue("-Dinstall.root=" + jonasroot); // java policy file String jonasConfigDir = jonasroot + File.separator + "config"; File javaPolicyFile = new File(jonasConfigDir, "java.policy"); if (javaPolicyFile.exists()) { genicTask.createJvmarg().setValue("-Djava.security.policy=" + javaPolicyFile.toString()); } // outputdir try { outputdir = createTempDir(); } catch (IOException aIOException) { String msg = "Cannot create temp dir: " + aIOException.getMessage(); throw new BuildException(msg, aIOException); } log("Using temporary output directory: " + outputdir, Project.MSG_VERBOSE); genicTask.createArg().setValue("-d"); genicTask.createArg().setFile(outputdir); for (String key : ejbFiles.keySet()) { File f = new File(outputdir + File.separator + key); f.getParentFile().mkdirs(); } log("Worked around a bug of GenIC 2.5.", Project.MSG_VERBOSE); // classpath Path classpath = getCombinedClasspath(); if (classpath == null) { classpath = new Path(getTask().getProject()); } classpath.append(new Path(classpath.getProject(), jonasConfigDir)); classpath.append(new Path(classpath.getProject(), outputdir.toString())); // try to create the classpath for the correct ORB if (orb != null) { String orbJar = jonasroot + File.separator + "lib" + File.separator + orb + "_jonas.jar"; classpath.append(new Path(classpath.getProject(), orbJar)); } log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE); genicTask.setClasspath(classpath); String genicClass; // GenIC class (3 are supported for various // versions // work around a bug of GenIC 2.5 // class name (search in the classpath provided for the ejbjar element) genicClass = getGenicClassName(classpath); if (genicClass == null) { log("Cannot find GenIC class in classpath.", Project.MSG_ERR); throw new BuildException("GenIC class not found, please check the classpath."); } log("Using '" + genicClass + "' GenIC class." , Project.MSG_VERBOSE); genicTask.setClassname(genicClass); // keepgenerated if (keepgenerated) { genicTask.createArg().setValue("-keepgenerated"); } // nocompil if (nocompil) { genicTask.createArg().setValue("-nocompil"); } // novalidation if (novalidation) { genicTask.createArg().setValue("-novalidation"); } // javac if (javac != null) { genicTask.createArg().setValue("-javac"); genicTask.createArg().setLine(javac); } // javacopts if (javacopts != null && !javacopts.equals("")) { genicTask.createArg().setValue("-javacopts"); genicTask.createArg().setLine(javacopts); } // rmicopts if (rmicopts != null && !rmicopts.equals("")) { genicTask.createArg().setValue("-rmicopts"); genicTask.createArg().setLine(rmicopts); } // secpropag if (secpropag) { genicTask.createArg().setValue("-secpropag"); } // verbose if (verbose) { genicTask.createArg().setValue("-verbose"); } // additionalargs if (additionalargs != null) { genicTask.createArg().setValue(additionalargs); } // the generated classes must not be added in the generic JAR! // is that buggy on old JOnAS (2.4) ?? genicTask.createArg().setValue("-noaddinjar"); // input file to process by GenIC genicTask.createArg().setValue(genericJarFile.getPath()); // calling GenIC task log("Calling " + genicClass + " for " + getConfig().descriptorDir + File.separator + descriptorName + ".", Project.MSG_VERBOSE); if (genicTask.executeJava() != 0) { // the method deleteOnExit() do not work because the directory is not empty log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE); deleteAllFiles(outputdir); if (!keepgeneric) { log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE); genericJarFile.delete(); } throw new BuildException("GenIC reported an error."); } // add the generated files to the ejbFiles addAllFiles(outputdir, "", ejbFiles); } /** * Get the GenIC class name to use in the given classpath. * * @param classpath classpath where the GenIC class must be searched. * @return the GenIC class name. Return <code>null</code> if the class name * cannot be found. */ String getGenicClassName(Path classpath) { log("Looking for GenIC class in classpath: " + classpath.toString(), Project.MSG_VERBOSE); try (AntClassLoader cl = classpath.getProject().createClassLoader(classpath)) { try { cl.loadClass(JonasDeploymentTool.GENIC_CLASS); log("Found GenIC class '" + JonasDeploymentTool.GENIC_CLASS + "' in classpath.", Project.MSG_VERBOSE); return JonasDeploymentTool.GENIC_CLASS; } catch (ClassNotFoundException cnf1) { log("GenIC class '" + JonasDeploymentTool.GENIC_CLASS + "' not found in classpath.", Project.MSG_VERBOSE); } try { cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_1); log("Found GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1 + "' in classpath.", Project.MSG_VERBOSE); return JonasDeploymentTool.OLD_GENIC_CLASS_1; } catch (ClassNotFoundException cnf2) { log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1 + "' not found in classpath.", Project.MSG_VERBOSE); } try { cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_2); log("Found GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2 + "' in classpath.", Project.MSG_VERBOSE); return JonasDeploymentTool.OLD_GENIC_CLASS_2; } catch (ClassNotFoundException cnf3) { log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2 + "' not found in classpath.", Project.MSG_VERBOSE); } } return null; } /** * Verify the configuration. * @param descriptorFileName the name of the descriptor file. * @param saxParser not used. * @throws BuildException if there is an error. */ @Override protected void checkConfiguration(String descriptorFileName, SAXParser saxParser) throws BuildException { // jonasroot if (jonasroot == null) { throw new BuildException("The jonasroot attribute is not set."); } if (!jonasroot.isDirectory()) { throw new BuildException( "The jonasroot attribute '%s' is not a valid directory.", jonasroot); } // orb final List<String> validOrbs = Arrays.asList(RMI_ORB, JEREMIE_ORB, DAVID_ORB); if (orb != null && !validOrbs.contains(orb)) { throw new BuildException( "The orb attribute '%s' is not valid (must be one of %s.", orb, validOrbs); } // additionalargs if (additionalargs != null && additionalargs.isEmpty()) { throw new BuildException("Empty additionalargs attribute."); } // javac if (javac != null && javac.isEmpty()) { throw new BuildException("Empty javac attribute."); } } /* ----------------------------------------------------------------------------------- */ /* utilitary methods */ /* ----------------------------------------------------------------------------------- */ /** * Create a temporary directory for GenIC output. * * @return the temp directory. * @throws BuildException if a temp directory cannot be created. */ private File createTempDir() throws IOException { return Files.createTempDirectory("genic").toFile(); } /** * Delete a file. If the file is a directory, delete recursively all the * files inside. * * @param aFile file to delete. */ private void deleteAllFiles(File aFile) { if (aFile.isDirectory()) { for (File child : aFile.listFiles()) { deleteAllFiles(child); } } aFile.delete(); } /** * Add a file to the a given hashtable. If the file is a directory, add * recursivly all the files inside to the hashtable. * * @param file the file to add. * @param rootDir the current sub-directory to scan. * @param hashtable the hashtable where to add the files. */ private void addAllFiles(File file, String rootDir, Hashtable<String, File> hashtable) { if (!file.exists()) { throw new IllegalArgumentException(); } String newRootDir; if (file.isDirectory()) { for (File child : file.listFiles()) { if (rootDir.isEmpty()) { newRootDir = child.getName(); } else { newRootDir = rootDir + File.separator + child.getName(); } addAllFiles(child, newRootDir, hashtable); } } else { hashtable.put(rootDir, file); } } }