/*
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);
}
}