/* ====================================================================
* The LateralNZ Software License, Version 1.0
*
* Copyright (c) 2003 LateralNZ. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* LateralNZ (http://www.lateralnz.org/) and other third parties."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "LateralNZ" must not be used to endorse or promote
* products derived from this software without prior written
* permission. For written permission, please
* contact oss@lateralnz.org.
*
* 5. Products derived from this software may not be called "Panther",
* or "Lateral" or "LateralNZ", nor may "PANTHER" or "LATERAL" or
* "LATERALNZ" appear in their name, without prior written
* permission of LateralNZ.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of LateralNZ. For more
* information on Lateral, please see http://www.lateralnz.com/ or
* http://www.lateralnz.org
*
*/
package org.lateralnz.panther.deploy;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.w3c.dom.Document;
import org.lateralnz.common.util.Constants;
import org.lateralnz.common.util.ObjectUtils;
import org.lateralnz.common.util.StringUtils;
import org.lateralnz.common.util.XMLUtils;
import org.lateralnz.panther.util.EJBConstants;
/**
* @author J R Briggs
*/
public class SessionBeanGenerator implements Constants, Serializable {
private String ejbjarXML;
private String tempdir;
private String classesdir;
private String wrapperTemplate;
private String homeWrapperTemplate;
private String wrapperTemplateDir;
private String logfile;
/**
* set the EJBJarXML property
*/
public void setEJBJarXML(String ejbjarXML) {
this.ejbjarXML = ejbjarXML;
}
/**
* set the temporary/working directory
*/
public void setTempDir(String tempdir) {
this.tempdir = StringUtils.toDirectory(tempdir);
}
/**
* set the directory where classes can be found
*/
public void setClassesDir(String classesdir) {
this.classesdir = StringUtils.toDirectory(classesdir);
}
/**
* set the name of the sessionbean wrapper template
*/
public void setWrapperTemplate(String wrapperTemplate) {
this.wrapperTemplate = wrapperTemplate;
}
public void setHomeWrapperTemplate(String homeWrapperTemplate) {
this.homeWrapperTemplate = homeWrapperTemplate;
}
/**
* set the directory where we can find the template
*/
public void setWrapperTemplateDir(String wrapperTemplateDir) {
this.wrapperTemplateDir = wrapperTemplateDir;
}
public void setLogFile(String logfile) {
this.logfile = logfile;
}
/**
* execute this task
*/
public void execute() throws Exception {
VelocityEngine ve = new VelocityEngine();
ve.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, wrapperTemplateDir);
if (!StringUtils.isEmpty(logfile)) {
ve.setProperty(VelocityEngine.RUNTIME_LOG, logfile);
}
ve.init();
Template ejbWrapper = ve.getTemplate(wrapperTemplate);
Template homeWrapper = ve.getTemplate(homeWrapperTemplate);
File f = new File(ejbjarXML);
String xml = StringUtils.readFromFile(ejbjarXML);
Document doc = XMLUtils.parse(xml);
File fworkingDir = new File(tempdir);
// various lists needed during the parse
List ejbwrappers = new ArrayList();
// work through the session descriptors
List l = EJBJarUtils.getSessionDescriptors(doc);
Iterator iter = l.iterator();
while (iter.hasNext()) {
SessionDescriptor sd = (SessionDescriptor)iter.next();
if (sd.getSessionType().equalsIgnoreCase(EJBConstants.STATEFUL)) {
iter.remove();
System.out.println("note: stateful bean " + sd.getEJBName() + " is not supported");
continue;
}
// get the remote, ejb and home classes
Class remote = Class.forName(sd.getEJBRemoteClass());
Class home = Class.forName(sd.getEJBHomeClass());
Class ejb = Class.forName(sd.getEJBClass());
String packageName = sd.getEJBRemoteClass().substring(0, sd.getEJBRemoteClass().lastIndexOf('.'));
String packageDir = StringUtils.replace(packageName, DOT, FILE_SEPARATOR);
checkDirStructure(tempdir, packageDir);
String[] tmp = StringUtils.toArray(remote.getName(), DOT);
// this is what we'll call our ejb wrapper
String wrapperName = tmp[tmp.length - 1] + "Impl";
String homeName = tmp[tmp.length - 1] + "HomeImpl";
String wrapperFullName = StringUtils.replace(sd.getEJBName(), FILE_SEPARATOR, UNDERSCORE);
// this is the classname and class signature file
String className = classesdir + packageDir + FILE_SEPARATOR + wrapperName + ".class";
String signatureFile = tempdir + packageDir + FILE_SEPARATOR + wrapperName + ".sig";
// get signatures for comparison (if they're not the same, we'll need to regenerate)
String oldsig = StringUtils.readFromFile(signatureFile);
String newsig = ObjectUtils.getMD5Signature(remote);
// check if we have already generated the class or if the signatures are different
File tst = new File(className);
String logtmp = EMPTY + (tst.exists() ? ONE : ZERO) + (tst.canRead() ? ONE : ZERO) + (newsig.equals(oldsig) ? ONE : ZERO);
if (!tst.exists() || !tst.canRead() || !newsig.equals(oldsig)) {
// get all remote methods and add to a list
Method[] methods = remote.getDeclaredMethods();
ArrayList methodList = new ArrayList();
for (int i = 0; i < methods.length; i++) {
if (!ObjectUtils.containsObject(methods[i].getExceptionTypes(), java.rmi.RemoteException.class)) {
continue;
}
boolean trans = false;
String tt = sd.getTransEntry(methods[i].getName());
if (tt.equalsIgnoreCase(REQUIRED)
|| tt.equalsIgnoreCase(REQUIRES_NEW)) {
trans = true;
}
methodList.add(new MethodDescriptor(methods[i], trans, tt));
}
// velocity generation of java code
VelocityContext ctx = new VelocityContext();
ctx.put("ejbpackage", packageName);
ctx.put("ejbname", wrapperFullName);
ctx.put("ejbwrapper", wrapperName);
ctx.put("ejbhomewrapper", homeName);
ctx.put("ejb", ejb.getName());
ctx.put("ejbremote", remote.getName());
ctx.put("ejbhome", home.getName());
ctx.put("methods", methodList);
ctx.put("generator", this);
System.out.println("note: generating source for " + remote.getName() + " [" + logtmp + "]");
// write to file
FileWriter fw = new FileWriter(tempdir + packageDir + FILE_SEPARATOR + wrapperName + ".java");
ejbWrapper.merge(ctx, fw);
fw.flush();
fw.close();
fw = new FileWriter(tempdir + packageDir + FILE_SEPARATOR + homeName + ".java");
homeWrapper.merge(ctx, fw);
fw.flush();
fw.close();
// write the new signature
tst = new File(signatureFile);
tst.delete();
fw = new FileWriter(signatureFile);
fw.write(newsig);
fw.flush();
fw.close();
}
}
}
/**
* check the directory structure for a package in a working directory, and if it
* doesn't exist, then create it. For example, the call: <br />
* checkDirStructure("test", "org/lateralnz/whatever");<br/>
* will create:<br/>
* test/org/lateralnz/whatever<br/>
* if it does not already exist
*/
protected void checkDirStructure(String workingDir, String packageDir) throws IOException {
File f = new File(workingDir + packageDir);
if (f.exists()) {
return;
}
StringBuffer sb = new StringBuffer(workingDir);
StringTokenizer st = new StringTokenizer(packageDir, FILE_SEPARATOR);
while (st.hasMoreTokens()) {
String next = st.nextToken();
sb.append(next).append(FILE_SEPARATOR);
f = new File(sb.toString());
if (!f.exists()) {
if (!f.mkdir()) {
throw new IOException("error creating directory : " + sb.toString());
}
}
}
}
/**
* returns the length of an object if it is a List or Array. <br/>
* NOTE: needs to be public otherwise the velocity engine can't get access
* to it
* @param an object that is a list or an array
* @return the length of the list or array
*/
public int getLength(Object obj) {
try {
if (obj instanceof List) {
return ((List)obj).size();
}
else {
return java.lang.reflect.Array.getLength(obj);
}
}
catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* so this task can be called standalone
*/
public static final void main(String[] args) throws Exception {
SessionBeanGenerator sbg = new SessionBeanGenerator();
sbg.setEJBJarXML(args[0]);
sbg.setTempDir(args[1]);
sbg.setClassesDir(args[2]);
sbg.setWrapperTemplate(args[3]);
sbg.setHomeWrapperTemplate(args[4]);
sbg.setWrapperTemplateDir(args[5]);
sbg.setLogFile(args[6]);
sbg.execute();
}
}
|