/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of either the GNU General Public License Version 2 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the License at http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language governing permissions and limitations under the License. When distributing the software, include this License Header Notice in each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the GPL Version 2 section of the License file that accompanied this code. If applicable, add the following below the License Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" Contributor(s): The Original Software is NetBeans. The Initial Developer of the Original Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. If you wish your version of this file to be governed by only the CDDL or only the GPL Version 2, indicate your decision by adding "[Contributor] elects to include this software in this distribution under the [CDDL or GPL Version 2] license." If you do not indicate a single choice of license, a recipient has the option to distribute your version of this file under either the CDDL, the GPL Version 2 or to extend the choice of license to its licensees as provided above. However, if you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then the option applies only if the new code is made subject to such option by the copyright holder. */ import com.sun.javatest.Status; import com.sun.jmitck.vendorwrapper.Generator; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Iterator; import java.util.Map; import java.util.Properties; import javax.jmi.model.ModelPackage; import javax.jmi.model.MofPackage; import javax.jmi.reflect.RefPackage; import javax.jmi.xmi.MalformedXMIException; import javax.jmi.xmi.XmiReader; import org.apache.log4j.Logger; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Javac; import org.apache.tools.ant.types.Path; import org.netbeans.api.mdr.CreationFailedException; import org.netbeans.api.mdr.JMIMapper; import org.netbeans.api.mdr.MDRManager; import org.netbeans.api.mdr.MDRepository; import org.netbeans.lib.jmi.mapping.FileStreamFactory; import org.openide.ErrorManager; import org.openide.util.Lookup; /** * Interacting with JMI TCK for the code generation * features of Netbeans MDR implementation of JMI. */ public class NBMDRGenerator implements Generator { private static final String PROP_COMPILED = "mdrjmi.compiled"; private static final String PROP_JMI_MAPPER = "mdrjmi.mapper"; private static final String DEF_JMI_MAPPER = "org.netbeans.lib.jmi.mapping.JMIMapperImpl"; private MDRepository repository; private File fileRootDir; private File generatedFileDir; private int increment = 0; private static final Logger log = Logger.getLogger(NBMDRGenerator.class); public NBMDRGenerator() { } /** * Perform any required iniialization of the generator. * MDR will generate JMI files to $TCKDIR/servers, thus we will * have to input the same path in Javatest Configuration Interview. * * @param p properties loaded from a properties file, if provided * by the vendor (may be null) * @param rootDir the root directory for the Javatest execution */ public void init(Properties p, File rootDir) { log.debug("init started"); this.fileRootDir = rootDir; for (Iterator it = p.entrySet().iterator(); it.hasNext();) { Map.Entry prop = (Map.Entry) it.next(); System.setProperty((String) prop.getKey(), (String) prop.getValue()); } // mimic RI TCK Wrapper, generate JMI to rootDir/servers String genFilePath = rootDir.toString() + File.separator + "servers"; log.debug("rootDir = " + rootDir); // location for generating JMI class files generatedFileDir = new File(genFilePath); generatedFileDir.mkdirs(); // genFilePath = generatedFileDir.getCanonicalPath(); // System.setProperty("Server.Directory", genFilePath); log.debug("genFilePath = " + genFilePath); repository = MDRManager.getDefault().getDefaultRepository(); log.debug("repository = " + repository); if (repository == null) throw new RuntimeException("Repository not found!"); } /** * Generate the code corresponding to the named metamodel. This * method must load the XMI file corresponding to the metamodel * name, and generate the corresponding byte code according to the * JMI specification. * * @param metamodel a fully-qualified name of the metamodel, which * will correspond to an XMI file in the metamodels * directory * @param String xmiFileLocation file location of the XMI file * @param interfacesOnly [ignored by MDR] * true, when only the interfaces, enum * classes, and exceptions specified by the JMI need be generated. * When false, the implementation of those interfaces must also be * geneated. * @return a Status instance, set to either passed, if code * generation completed, or to error, if not. */ public Status generate(String metamodel, String xmiImportPath, boolean interfacesOnly) { try { log.debug("metamodel=" + metamodel); log.debug("xmi import path=" + xmiImportPath); loadXMIModel(metamodel, xmiImportPath); generateJMI(metamodel, generatedFileDir); if (System.getProperty(PROP_COMPILED) == null) { compileJMI(generatedFileDir); } } catch (Throwable t) { //t.printStackTrace(); log.error("", t); return Status.error(t.toString()); } return Status.passed("Code generated successfully for metamodel: " + metamodel); } /** * [Returns null since MDR has no such dependency.] * * Returns the classpath required for the generated code. This * method only needs to create the classpath if the classpath of * generated code changes from one generation execution to the * next. For instance, if the implemention places the generated * code in a jar file named after the metamodel, this method would * be invoked. The choice whether this method is invoked or not is * controlled by a configuration parameter set in the configuration * editor. * * @param metamodel the fully-qualified name of the metamodel (the * same name that is passed in to the generate method). * @return a classpath, using the classpath separators appropriate * for the current platform */ public String getMetamodelLoadPath(String metamodel) { // return "servers:netbeans/nbmdr.jar"; // let the Configuration Editor takes care of it; return null; } /** * Returns a new RefPackage, returning a unique identifier, which * can be used to access the new RefPackage. * Pre-condition for MDR: the generated interfaces must be contained in the * $CLASSPATH. * * @param packageName the name of the Model.Package defining the RefPackage * @param prefix the package prefix used in code generation (as * defined by a tag in the metamodel) * @return RefPackage newly created instance, null if error occured */ public RefPackage createRefPackage(String packageName, String prefix) { String names[] = repository.getExtentNames(); // iterate through all instances for (int i = 0; i < names.length; i++) { RefPackage extent = repository.getExtent(names[i]); // check whether it is instance of MOF model if (extent instanceof ModelPackage) { ModelPackage pkg = (ModelPackage) extent; // iterate through all the packages in the instance for (Iterator it = pkg.getMofPackage().refAllOfClass().iterator(); it.hasNext();) { MofPackage temp = (MofPackage) it.next(); // check whether the package is of the searched name and whether it is outermost if (temp.getName().equals(packageName) && temp.getContainer() == null) try { // create and return a new instance of the package return repository.createExtent("Instance #" + (increment++), temp); } catch (CreationFailedException cfe) { ErrorManager.getDefault().notify(cfe); log.error("", cfe); throw new RuntimeException(cfe.getMessage()); } } } } // package that has to be instantiated could not be found throw new RuntimeException("Package " + packageName + " not found."); } /** * Returns an instance of XmiReader. * @return XmiReader instance */ public XmiReader createXmiReader() { return (XmiReader) Lookup.getDefault().lookup(XmiReader.class); } /** * [No action for MDR] * Clean-up after a list of RefPackage instances, after completion * of a test. An implementation is given the oportunity to remove * objects created in the extents as the result of the test, or * perform any other useful cleanup. No action is necessary; * however, cleanup activities may avoid issues such as running out * of memory, failing subsequent tests due to a previous test * failure that corrupts an extent, etc. * * @param refPackages a list of the RefPackages that were created for a test. */ public void retire(RefPackage refPackage) { } /** * Returns a top-level RefPackage, as idenitified by the argument. * The top-level RefPackage corresponds to a top-level * Model.Package in a metamodel. The identifier may be * implementation-specific, but must uniquely identify the * RefPackage. If the RefPackage does not exist, the * implementation may either return null, or create a new instance * and return it. * * @param id uniquely identifies a RefPackage * @return RefPackage instance according to id */ private RefPackage getRepository(String id) { return repository.getExtent(id); } /** * Load a metamodel from XMI file to the default repository under name. * @param String package name * @param String xmiFilename * @exception Exception wrapper for CreationFailedException, * IOException, and MalformedXMIException */ private void loadXMIModel(String name, String xmiFilename) throws Exception { // is model loaded already? //log.debug("loadXMIModel(name=" + name + ", xmiFilename=" + xmiFilename + ")"); RefPackage mp = getRepository(name); if(mp == null) { try { //log.debug("repository = " + repository); log.debug("xmiFilename = " +xmiFilename); //URL xmiURL = NBMDRGenerator.class.getResource(xmiFilename); File xmiFile = new File(xmiFilename); URL xmiURL = null; if(xmiFile != null) { xmiURL = xmiFile.toURL(); } else { throw new Exception("NullPointerException: filename " + xmiFilename + " not found! loadXMIModel failed."); } log.debug("xmiURL= " + xmiURL); String xmiURLString = null; if(xmiURL != null) { xmiURLString = xmiURL.toString(); } else { throw new Exception("NullPointerException: filename " + xmiFilename + " not found! loadXMIModel failed."); } log.debug("xmiURLString = " +xmiURLString); RefPackage model = repository.createExtent(name); XmiReader xmireader = createXmiReader(); xmireader.read(xmiURLString , model); } catch (CreationFailedException cfe) { ErrorManager.getDefault().notify(cfe); throw new Exception("CreationFailedException: Could not read the required XMI file. " + cfe.getMessage()); } catch (IOException ioe) { ErrorManager.getDefault().notify(ioe); throw new Exception("IOException: Could not read the required XMI file. " + ioe.getMessage()); } catch (MalformedXMIException xmie) { ErrorManager.getDefault().notify(xmie); throw new Exception("SAXException: Could not read the required XMI file. " + xmie.getMessage()); } } //else Model has probably been loaded } /** * Generate JMI classes accordinig to pkgName stored in MDR. * @param String pkgName * @param File genFilePath * @exception Exception wrapper for java.beans.PropertyVetoException * and java.io.IOException */ private void generateJMI(String pkgName, File genFilePath) throws Exception { try { ModelPackage pkg = (ModelPackage) getRepository(pkgName); JMIMapper jmiMapper = (JMIMapper) Class.forName(System.getProperty(PROP_JMI_MAPPER, DEF_JMI_MAPPER)).newInstance(); ErrorManager.getDefault().log("JMI mapper: " + jmiMapper.getClass().getName()); jmiMapper.generate(new FileStreamFactory(genFilePath), pkg); } catch(Exception e) { ErrorManager.getDefault().notify(e); throw e; } } /** * Compile MDR generated JMI source files in genFilePath. * Use Jarkata Ant Project to compile recursively which will subsequently * call com.sun.tools.javac.Main. * Note: if there is any compilation error in one sub-directory, Ant would * bail out. * @param File genFilePath * @exception Exception wrapper for org.apache.tools.ant.BuildException */ private void compileJMI(File genFilePath) throws Exception { log.debug("Start compiling files in " + genFilePath); try { Javac antJavac = new Javac(); Project antProj = new Project(); antProj.init(); antJavac.setProject(antProj); antJavac.setSrcdir(new Path(null, genFilePath.toString())); antJavac.setDestdir(genFilePath); antJavac.setClasspath(new Path(null, System.getProperty("java.class.path"))); antJavac.execute(); // compile all Java source files in genFilePath } catch (BuildException buildEx) { ErrorManager.getDefault().notify(buildEx); log.error("", buildEx); throw new Exception("BuildException: Error occured when compiling. " + buildEx); } log.debug("Finished compiling files in " + genFilePath); } }