001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: /*
042: * ClientBeanGenerator.java
043: *
044: * Created on May 12, 2004, 8:36 PM
045: */
046:
047: package org.netbeans.modules.visualweb.ejb.load;
048:
049: import org.netbeans.modules.visualweb.ejb.datamodel.EjbInfo;
050: import org.netbeans.modules.visualweb.ejb.datamodel.MethodInfo;
051: import org.netbeans.modules.visualweb.ejb.datamodel.MethodParam;
052: import org.netbeans.modules.visualweb.ejb.datamodel.MethodReturn;
053: import org.netbeans.modules.visualweb.ejb.util.Util;
054: import java.io.File;
055: import java.io.FileOutputStream;
056: import java.io.PrintWriter;
057: import java.lang.reflect.Method;
058: import java.net.URLClassLoader;
059: import java.util.*;
060: import org.openide.ErrorManager;
061:
062: /**
063: * Generates a wrapper bean for the given EJB session bean
064: * @author cao
065: */
066: public class ClientBeanGenerator {
067: /**
068: * The EJB which this class is going to create wrapper class for
069: */
070: private EjbInfo ejbInfo;
071:
072: /**
073: * Whether create() can be automatically called in the business wrapper method
074: */
075: private boolean autoInit = true;
076:
077: /**
078: * To load the classes in the client jars
079: */
080: private URLClassLoader classLoader;
081:
082: /**
083: * These are not real properties. The reason we want them
084: * to look like JavaBean properties is because we can only
085: * bind properties to JSF components currently. They should
086: * be removed once we can support method binding
087: */
088: private ArrayList virtualProperties;
089:
090: /**
091: * Creates a new instance of ClientBeanGenerator
092: *
093: * @param ejbInfo The EJB which the bean and BeanInfo wrapper class are for
094: * @param port Where the Ejb can be looked up
095: */
096: public ClientBeanGenerator(EjbInfo ejbInfo,
097: URLClassLoader classloader) {
098: this .ejbInfo = ejbInfo;
099: this .classLoader = classloader;
100: this .virtualProperties = new ArrayList();
101: }
102:
103: /*
104: * Generates the bean and BeanInfo class for the EJB and also compile the class
105: *
106: * @param srcDir Where the generated .java and .class will be saved
107: * @return a Collection of ClassDescriptors describing the classes (.java and .class)
108: * generated by this method
109: */
110: public Collection generateClasses(String srcDir)
111: throws EjbLoadException {
112: // First, generate the bean class source code
113: ClassDescriptor beanClassDescriptor = generateBeanClasse(srcDir);
114:
115: // Take care the case where there is no package
116: String beanClassName = beanClassDescriptor.getPackageName()
117: + "." + beanClassDescriptor.getClassName();
118: if (beanClassDescriptor.getPackageName() == null
119: || beanClassDescriptor.getPackageName().length() == 0)
120: beanClassName = beanClassDescriptor.getClassName();
121:
122: ejbInfo.setBeanWrapperName(beanClassName);
123:
124: // Now, Generate the BeanInfo class source code
125: ClientBeanInfoGenerator infoGenerator = new ClientBeanInfoGenerator(
126: ejbInfo.getJNDIName(), beanClassName, virtualProperties);
127: ClassDescriptor beanInfoClassDescriptor = infoGenerator
128: .generateClass(srcDir);
129:
130: // Take care the case where there is no package
131: String beanInfoClassName = beanInfoClassDescriptor
132: .getPackageName()
133: + "." + beanInfoClassDescriptor.getClassName();
134: if (beanInfoClassDescriptor.getPackageName() == null
135: || beanInfoClassDescriptor.getPackageName().length() == 0)
136: beanInfoClassName = beanInfoClassDescriptor.getClassName();
137:
138: ejbInfo.setBeanInfoWrapperName(beanInfoClassName);
139:
140: // Done! Return all the class descriptors
141:
142: ArrayList classDescriptors = new ArrayList();
143: classDescriptors.add(beanClassDescriptor);
144: classDescriptors.add(beanInfoClassDescriptor);
145:
146: return classDescriptors;
147: }
148:
149: /**
150: * Generates the Bean class for the EJB.
151: *
152: * @param srcDir where the java source code will be saved
153: * @return The ClassDescriptor of the bean class just generated
154: */
155: private ClassDescriptor generateBeanClasse(String srcDir)
156: throws EjbLoadException {
157: // Declare it outside the try-catch so that the file name can be logged in case of exception
158: File javaFile = null;
159:
160: try {
161: // Figure out the package name, class names, and directory, file name
162:
163: int i = ejbInfo.getCompInterfaceName().lastIndexOf('.');
164: String className = ejbInfo.getCompInterfaceName()
165: .substring(i + 1);
166:
167: // Get the package name. The wrapper bean will be in the same package
168: //String packageName = EjbLoader.CLIENT_WRAPPER_PACKAGE_NAME + "." + className.toLowerCase();
169:
170: // The package for all the generated class for this ejb is <packageForThisEjb>.<ejbremoteinterfacename>
171: if (i == -1) // no package name
172: i = 0;
173: String packageName = ejbInfo.getCompInterfaceName()
174: .substring(0, i)
175: + "." + className.toLowerCase();
176:
177: // Home interface name
178: String homeName = ejbInfo.getHomeInterfaceName()
179: .substring(
180: ejbInfo.getHomeInterfaceName().lastIndexOf(
181: '.') + 1);
182:
183: // The Wrapper bean class name - <remote>Cleint
184: String clientBeanClassName = className + "Client"; // NOI18N
185:
186: String classDir = packageName.replace('.',
187: File.separatorChar);
188: File dirF = new File(srcDir + File.separator + classDir);
189: if (!dirF.exists()) {
190: if (!dirF.mkdirs())
191: System.out.println(".....failed to make dir"
192: + srcDir + File.separator + classDir);
193: }
194:
195: String clientBeanClassFile = clientBeanClassName + ".java"; // NOI18N
196: javaFile = new File(dirF, clientBeanClassFile);
197: javaFile.createNewFile();
198:
199: ClassDescriptor beanClassDescriptor = new ClassDescriptor(
200: clientBeanClassName, // class name
201: packageName, // package name
202: javaFile.getAbsolutePath(), // full path java file name
203: classDir + File.separator + clientBeanClassFile); // file name with package in path
204:
205: // Generate java code
206:
207: PrintWriter out = new PrintWriter(new FileOutputStream(
208: javaFile));
209:
210: // package
211: if (packageName != null && packageName.length() != 0) {
212: out.println("package " + packageName + ";");
213: out.println();
214: }
215:
216: // comments
217: out.println("/**");
218: out.println(" * Source code created on " + new Date());
219: out.println(" */");
220: out.println();
221:
222: // Do not import if the remote interface has no package
223: if (ejbInfo.getCompInterfaceName().indexOf('.') != -1)
224: out.println("import " + ejbInfo.getCompInterfaceName()
225: + ";");
226:
227: // Do not import if the home interface has no package
228: if (ejbInfo.getHomeInterfaceName().indexOf('.') != -1)
229: out.println("import " + ejbInfo.getHomeInterfaceName()
230: + ";");
231:
232: out.println();
233:
234: // start of class
235: out.println("public class " + clientBeanClassName
236: + " implements java.io.Serializable {");
237: out.println();
238:
239: // private memeber variables
240: String classVariable = className.toLowerCase();
241: String homeVariable = homeName.toLowerCase();
242: out.println(" private " + className + " "
243: + classVariable + ";");
244: out.println(" private " + homeName + " " + homeVariable
245: + ";");
246: out.println(" private boolean initialized = false;");
247: out.println();
248:
249: // Constructor
250: out.println(" public " + clientBeanClassName + "() {");
251: out.println(" }");
252: out.println();
253:
254: // create() methods
255: // For stateless session ejbs, there is always one create() without argument.
256: // But it is not true for stateful session ejbs, which can have many create() methods with
257: // all kinds of argumentst
258: createInitMethod(out, homeVariable, homeName, classVariable);
259:
260: // Business methods
261: for (Iterator iter = ejbInfo.getMethods().iterator(); iter
262: .hasNext();) {
263: MethodInfo methodInfo = (MethodInfo) iter.next();
264:
265: if (!methodInfo.isBusinessMethod())
266: continue;
267:
268: // Check whether the method fits into the JavaBean pattern
269: // If yes, we'll make a virtual property out of it so that
270: // it can be property-binding to a JSF component
271: virtualPropertyOrNot(methodInfo);
272:
273: out.println(" /**");
274: out.println(" * @see "
275: + javaDocMethod(ejbInfo.getCompInterfaceName(),
276: methodInfo));
277: out.println(" */");
278: out
279: .println(" " + methodSignature(methodInfo)
280: + " {");
281:
282: // If design time, return fake data if the method reutrn something (!void)
283: out
284: .println(" if( java.beans.Beans.isDesignTime() ) { ");
285: if (methodInfo.getReturnType().getClassName().equals(
286: "void")) // NOI18N
287: out.println(" return;");
288: else
289: out.println(" return "
290: + fakeReturnValue(methodInfo) + ";");
291: out.println(" }");
292: out.println();
293:
294: // create() will be automatically called if the autoInit is true.
295: // Otherwise, check whether the bean is initialized or not
296: if (autoInit)
297: out.println(" create();");
298: else {
299: out
300: .println(" if( initialized == false ) { ");
301: out
302: .println(" throw new java.lang.RuntimeException( \"Bean not initialized\" );");
303: out.println(" }");
304: }
305:
306: out.println(" "
307: + invokeMethodStatement(classVariable,
308: methodInfo) + ";");
309: out.println(" }");
310: out.println();
311: }
312:
313: // End of client bean clas
314: out.println("}");
315:
316: out.flush();
317: out.close();
318:
319: return beanClassDescriptor;
320: } catch (java.io.FileNotFoundException ex) {
321: // Log error
322: String errMsg = "Error occurred when trying to generate the wrapper bean class for EJB "
323: + ejbInfo.getJNDIName()
324: + ". Could not find file "
325: + javaFile.getAbsolutePath();
326: ErrorManager
327: .getDefault()
328: .getInstance(
329: "org.netbeans.modules.visualweb.ejb.load.ClientBeanGenerator")
330: .log(errMsg);
331: ex.printStackTrace();
332:
333: // Throw up as a SYSTEM_ERROR
334: throw new EjbLoadException(ex.getMessage());
335: } catch (java.io.IOException ex) {
336: // Log error
337: String errMsg = "Error occurred when trying to generate the wrapper bean class for EJB "
338: + ejbInfo.getJNDIName()
339: + ". Could not create file "
340: + javaFile.getAbsolutePath();
341: ErrorManager
342: .getDefault()
343: .getInstance(
344: "org.netbeans.modules.visualweb.ejb.load.ClientBeanGenerator")
345: .log(errMsg);
346: ex.printStackTrace();
347:
348: // Throw up as a SYSTEM_ERROR
349: throw new EjbLoadException(ex.getMessage());
350: }
351: }
352:
353: private void virtualPropertyOrNot(MethodInfo method) {
354: String methodName = method.getName();
355:
356: if (methodName.startsWith("get")
357: && !method.getReturnType().getClassName()
358: .equals("void")
359: && method.getParameters().size() == 0) {
360: virtualProperties.add(methodName.substring(3));
361: }
362: }
363:
364: private void createInitMethod(PrintWriter out, String homeVariable,
365: String homeName, String classVariable) {
366:
367: for (Iterator iter = ejbInfo.getMethods().iterator(); iter
368: .hasNext();) {
369: MethodInfo methodInfo = (MethodInfo) iter.next();
370:
371: // One per create() method, not business method
372: if (methodInfo.isBusinessMethod())
373: continue;
374:
375: ArrayList parameters = methodInfo.getParameters();
376:
377: // Auto create() can not be called if there is one or more create()
378: // method takes arguments (stateful session ejb)
379: if (parameters.size() != 0)
380: autoInit = false;
381:
382: StringBuffer paramStr = new StringBuffer();
383: for (int i = 0; i < parameters.size(); i++) {
384: MethodParam p = (MethodParam) parameters.get(i);
385:
386: if (i != 0)
387: paramStr.append(", ");
388:
389: paramStr.append(p.getType());
390: paramStr.append(" ");
391: paramStr.append(p.getName());
392: }
393:
394: out.println(" /**");
395: out.println(" * @see "
396: + javaDocMethod(ejbInfo.getHomeInterfaceName(),
397: methodInfo));
398: out.println(" */");
399: out
400: .println(" public void create("
401: + paramStr
402: + ") throws javax.naming.NamingException, java.rmi.RemoteException, javax.ejb.CreateException {");
403: out
404: .println(" if( !java.beans.Beans.isDesignTime() && !initialized ) { ");
405: out
406: .println(" javax.naming.InitialContext ctx = new javax.naming.InitialContext();");
407: out.println(" Object objRef = ctx.lookup( \""
408: + "java:comp/env/" + ejbInfo.getWebEjbRef()
409: + "\" );");
410: out
411: .println(" "
412: + homeVariable
413: + " = ("
414: + homeName
415: + ")javax.rmi.PortableRemoteObject.narrow( objRef, "
416: + homeName + ".class );");
417: out.println(" " + classVariable + " = "
418: + invokeMethod(homeVariable, methodInfo) + ";");
419: out.println(" initialized = true;");
420: out.println(" }");
421: out.println(" }");
422: out.println();
423: }
424: }
425:
426: private String fakeReturnValue(MethodInfo method)
427: throws EjbLoadException {
428: String fakeReturn = "null";
429:
430: String returnType = method.getReturnType().getClassName();
431:
432: // Can be one of the following:
433: // int, long, double, float, short, byte
434: // char
435: // boolean
436: // void
437: if (returnType.equals("int") || returnType.equals("long")
438: || returnType.equals("double")
439: || returnType.equals("float")
440: || returnType.equals("short")
441: || returnType.equals("byte")) {
442: return "0";
443: } else if (returnType.equals("boolean")) {
444: return "false";
445: } else if (returnType.equals("char")) {
446: return "\'A\'";
447: } else if (returnType.indexOf("[]") != -1) {
448: // Return type is Array
449: return null;
450: } else {
451: try {
452: Class returnClass = Class.forName(returnType, true,
453: classLoader);
454:
455: if (returnClass == String.class)
456: return "\"ABC\"";
457: else if (returnClass == Collection.class)
458: return "new java.util.ArrayList()";
459: else if (returnClass == Set.class)
460: return "new java.util.HashSet()";
461: else
462: // Must be a single object return type (including Array)
463: return "null";
464: } catch (java.lang.ClassNotFoundException e) {
465: // Should never happen
466: // Log error no matter what
467: String errMsg = "Error occurred when trying to generate wrapper bean class for EJB "
468: + ejbInfo.getJNDIName()
469: + ". Could not find class "
470: + method.getReturnType().getClassName();
471: ErrorManager
472: .getDefault()
473: .getInstance(
474: "org.netbeans.modules.visualweb.ejb.load.ClientBeanGenerator")
475: .log(ErrorManager.ERROR, errMsg);
476: e.printStackTrace();
477:
478: // Throw up as SYSTEM_ERROR
479: throw new EjbLoadException(e.getMessage());
480: }
481: }
482: }
483:
484: private String javaDocMethod(String className, MethodInfo methodInfo) {
485: // Link to the EJB method - package.class#method(type, type, ...)
486: StringBuffer linkMethod = new StringBuffer();
487: linkMethod.append(className + "#");
488: linkMethod.append(methodInfo.getName());
489: linkMethod.append("(");
490: ArrayList parameters = methodInfo.getParameters();
491: for (int i = 0; i < parameters.size(); i++) {
492: MethodParam p = (MethodParam) parameters.get(i);
493:
494: if (i != 0)
495: linkMethod.append(", ");
496:
497: linkMethod.append(p.getType());
498: }
499:
500: linkMethod.append(")");
501:
502: return linkMethod.toString();
503: }
504:
505: /**
506: * Forms the method signature for the given method information
507: */
508: private String methodSignature(MethodInfo methodInfo) {
509: StringBuffer buf = new StringBuffer();
510:
511: buf.append("public ");
512: buf.append(methodInfo.getReturnType().getClassName());
513: buf.append(" ");
514: buf.append(methodInfo.getName());
515: buf.append("(");
516:
517: ArrayList parameters = methodInfo.getParameters();
518: for (int i = 0; i < parameters.size(); i++) {
519: MethodParam p = (MethodParam) parameters.get(i);
520:
521: if (i != 0)
522: buf.append(", ");
523:
524: buf.append(p.getType());
525: buf.append(" ");
526: buf.append(p.getName());
527: }
528:
529: buf.append(")");
530:
531: ArrayList exceptions = methodInfo.getExceptions();
532: if (exceptions != null && !exceptions.isEmpty()) {
533: buf.append(" throws ");
534:
535: for (int i = 0; i < exceptions.size(); i++) {
536: if (i != 0)
537: buf.append(", ");
538:
539: buf.append((String) exceptions.get(i));
540: }
541: }
542:
543: if (autoInit) {
544: // Since the create() is called before calling the business method,
545: // need to throw two more exceptions from the create():
546: // javax.naming.NamingException, javax.ejb.CreateException
547: buf.append(",");
548: buf
549: .append("javax.naming.NamingException, javax.ejb.CreateException");
550: }
551:
552: return buf.toString();
553: }
554:
555: /*
556: * Forms the method call string
557: */
558: private String invokeMethodStatement(String variableName,
559: MethodInfo methodInfo) {
560: StringBuffer buf = new StringBuffer();
561:
562: if (!methodInfo.getReturnType().getClassName().equals("void"))
563: buf.append("return ");
564:
565: buf.append("this.");
566: buf.append(variableName);
567: buf.append(".");
568: buf.append(methodInfo.getName());
569: buf.append("(");
570:
571: ArrayList parameters = methodInfo.getParameters();
572: for (int i = 0; i < parameters.size(); i++) {
573: MethodParam p = (MethodParam) parameters.get(i);
574:
575: if (i != 0)
576: buf.append(", ");
577:
578: buf.append(p.getName());
579: }
580:
581: buf.append(")");
582: return buf.toString();
583: }
584:
585: private String invokeMethod(String variableName,
586: MethodInfo methodInfo) {
587: StringBuffer buf = new StringBuffer();
588:
589: buf.append("this.");
590: buf.append(variableName);
591: buf.append(".");
592: buf.append(methodInfo.getName());
593: buf.append("(");
594:
595: ArrayList parameters = methodInfo.getParameters();
596: for (int i = 0; i < parameters.size(); i++) {
597:
598: MethodParam p = (MethodParam) parameters.get(i);
599:
600: if (i != 0)
601: buf.append(", ");
602:
603: buf.append(p.getName());
604: }
605:
606: buf.append(")");
607: return buf.toString();
608: }
609:
610: public static void main(String[] args) {
611: int i = 0;
612:
613: Integer ii = new Integer(0);
614:
615: Class returnClass = ii.getClass();
616:
617: }
618: }
|