/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 Oracle and/or its affiliates. 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 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. 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 packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * 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 don't 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. */ package com.sun.jdo.spi.persistence.support.ejb.codegen; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; import javax.inject.Inject; import javax.inject.Named; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import com.sun.enterprise.config.serverbeans.JavaConfig; import com.sun.enterprise.deployment.Application; import com.sun.jdo.spi.persistence.support.ejb.ejbc.CMPProcessor; import com.sun.jdo.spi.persistence.support.ejb.ejbc.JDOCodeGenerator; import com.sun.jdo.spi.persistence.support.sqlstore.ejb.EJBHelper; import com.sun.jdo.spi.persistence.utility.logging.Logger; import org.glassfish.api.admin.ServerEnvironment; import org.glassfish.api.deployment.DeploymentContext; import org.glassfish.deployment.common.DeploymentException; import org.glassfish.ejb.deployment.descriptor.EjbBundleDescriptorImpl; import org.glassfish.ejb.deployment.descriptor.EjbDescriptor; import org.glassfish.ejb.deployment.descriptor.IASEjbCMPEntityDescriptor; import org.glassfish.ejb.spi.CMPDeployer; import org.glassfish.persistence.common.I18NHelper; import org.jvnet.hk2.annotations.Optional; import org.jvnet.hk2.annotations.Service; /** * Generates concrete impls for CMP beans in an archive. * * @author Nazrul Islam * @since JDK 1.4 */ @Service public class CMPDeployerImpl implements CMPDeployer { @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) @Optional private JavaConfig javaConfig; /** * Generates the concrete impls for all CMPs in the application. * * @throws DeploymentException if this exception was thrown while generating concrete impls */ public void deploy(DeploymentContext ctx) throws DeploymentException { // deployment descriptor object representation for the archive Application application = null; // deployment descriptor object representation for each module EjbBundleDescriptorImpl bundle = null; // ejb name String beanName = null; // GeneratorException message if any StringBuffer generatorExceptionMsg = null; try { CMPGenerator gen = new JDOCodeGenerator(); // stubs dir for the current deployment (generated/ejb) File stubsDir = ctx.getScratchDir("ejb"); //NOI18N application = ctx.getModuleMetaData(Application.class); if (_logger.isLoggable(Logger.FINE)) { _logger.fine( "cmpc.processing_cmp", //NOI18N application.getRegistrationName()); } List<File> cmpFiles = new ArrayList<File>(); final ClassLoader jcl = application.getClassLoader(); bundle = ctx.getModuleMetaData(EjbBundleDescriptorImpl.class); // This gives the dir where application is exploded String archiveUri = ctx.getSource().getURI().getSchemeSpecificPart(); if (_logger.isLoggable(Logger.FINE)) { _logger.fine("[CMPC] Module Dir name is " //NOI18N + archiveUri); } // xml dir for the current deployment (generated/xml) String generatedXmlsPath = ctx.getScratchDir("xml").getCanonicalPath(); if (_logger.isLoggable(Logger.FINE)) { _logger.fine("[CMPC] Generated XML Dir name is " //NOI18N + generatedXmlsPath); } try { long start = System.currentTimeMillis(); gen.init(bundle, ctx, archiveUri, generatedXmlsPath); Iterator ejbs=bundle.getEjbs().iterator(); while ( ejbs.hasNext() ) { EjbDescriptor desc = (EjbDescriptor) ejbs.next(); beanName = desc.getName(); if (_logger.isLoggable(Logger.FINE)) { _logger.fine("[CMPC] Ejb Class Name: " //NOI18N + desc.getEjbClassName()); } if ( desc instanceof IASEjbCMPEntityDescriptor) { // generate concrete CMP class implementation IASEjbCMPEntityDescriptor entd = (IASEjbCMPEntityDescriptor)desc; if (_logger.isLoggable(Logger.FINE)) { _logger.fine( "[CMPC] Home Object Impl name is " //NOI18N + entd.getLocalHomeImplClassName()); } // The classloader needs to be set else we fail down the road. ClassLoader ocl = entd.getClassLoader(); entd.setClassLoader(jcl); try { gen.generate(entd, stubsDir, stubsDir); } catch (GeneratorException e) { String msg = e.getMessage(); _logger.warning(msg); generatorExceptionMsg = addGeneratorExceptionMessage( msg, generatorExceptionMsg); } finally { entd.setClassLoader(ocl); } /* WARNING: IASRI 4683195 * JDO Code failed when there was a relationship involved * because it depends upon the orginal ejbclasname and hence * this code is shifted to just before the Remote Impl is * generated.Remote/Home Impl generation depends upon this * value */ } } // end while ejbs.hasNext() beanName = null; cmpFiles.addAll(gen.cleanup()); long end = System.currentTimeMillis(); _logger.fine("CMP Generation: " + (end - start) + " msec"); } catch (GeneratorException e) { String msg = e.getMessage(); _logger.warning(msg); generatorExceptionMsg = addGeneratorExceptionMessage(msg, generatorExceptionMsg); } bundle = null; // Used in exception processing // Compile the generated classes if (generatorExceptionMsg == null) { long start = System.currentTimeMillis(); compileClasses(ctx, cmpFiles, stubsDir); long end = System.currentTimeMillis(); _logger.fine("Java Compilation: " + (end - start) + " msec"); // Do Java2DB if needed start = System.currentTimeMillis(); CMPProcessor processor = new CMPProcessor(ctx); processor.process(); end = System.currentTimeMillis(); _logger.fine("Java2DB processing: " + (end - start) + " msec"); _logger.fine( "cmpc.done_processing_cmp", application.getRegistrationName()); } } catch (GeneratorException e) { _logger.warning(e.getMessage()); throw new DeploymentException(e); } catch (Throwable e) { String eType = e.getClass().getName(); String appName = application.getRegistrationName(); String exMsg = e.getMessage(); String msg = null; if (bundle == null) { // Application or compilation error msg = I18NHelper.getMessage(messages, "cmpc.cmp_app_error", eType, appName, exMsg); } else { String bundleName = bundle.getModuleDescriptor().getArchiveUri(); if (beanName == null) { // Module processing error msg = I18NHelper.getMessage(messages, "cmpc.cmp_module_error", new Object[] {eType, appName, bundleName, exMsg}); } else { // CMP bean generation error msg = I18NHelper.getMessage(messages, "cmpc.cmp_bean_error", new Object[] {eType, beanName, appName, bundleName, exMsg}); } } _logger.log(Logger.SEVERE, msg, e); throw new DeploymentException(msg); } if (generatorExceptionMsg != null) { // We already logged each separate part. throw new DeploymentException(generatorExceptionMsg.toString()); } } /** * Integration point for cleanup on undeploy or failed deploy. */ public void clean(DeploymentContext ctx) { CMPProcessor processor = new CMPProcessor(ctx); processor.clean(); } /** * Integration point for application unload */ public void unload(ClassLoader cl) { try { EJBHelper.notifyApplicationUnloaded(cl); } catch (Exception e) { _logger.log(Logger.WARNING, "cmpc.cmp_cleanup_problems", e); } } /** * Compile .java files. * * @param ctx DeploymentContext associated with the call * @param files actual source files * @param destDir destination directory for .class files * * @exception GeneratorException if an error while code compilation */ private void compileClasses(DeploymentContext ctx, List<File> files, File destDir) throws GeneratorException { if (files.isEmpty() ) { return; } // class path for javac String classPath = ctx.getTransientAppMetaData(CMPDeployer.MODULE_CLASSPATH, String.class); List<String> options = new ArrayList<String>(); if (javaConfig!=null) { options.addAll(javaConfig.getJavacOptionsAsList()); } StringBuffer msgBuffer = new StringBuffer(); boolean compilationResult = false; try { // add the rest of the javac options options.add("-d"); options.add(destDir.toString()); options.add("-classpath"); options.add(System.getProperty("java.class.path") //TODO do we need to add java.class.path for compilation? + File.pathSeparator + classPath); if (_logger.isLoggable(Logger.FINE)) { for(File file : files) { _logger.fine(I18NHelper.getMessage(messages, "cmpc.compile", file.getPath())); } StringBuffer sbuf = new StringBuffer(); for ( String s : options) { sbuf.append("\n\t").append(s); } _logger.fine("[CMPC] JAVAC OPTIONS: " + sbuf.toString()); } // Using Java 6 compiler API to compile the generated .java files JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); StandardJavaFileManager manager = compiler.getStandardFileManager(diagnostics, null, null); Iterable compilationUnits = manager.getJavaFileObjectsFromFiles(files); long start = System.currentTimeMillis(); long end = start; compilationResult = compiler.getTask( null, manager, diagnostics, options, null, compilationUnits).call(); end = System.currentTimeMillis(); _logger.fine("JAVA compile time (" + files.size() + " files) = " + (end - start)); // Save compilation erros in msgBuffer to be used in case of failure for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { //Ignore NOTE about generated non safe code if (diagnostic.getKind().equals(Diagnostic.Kind.NOTE)) { if (_logger.isLoggable(Logger.FINE)) { msgBuffer.append("\n").append(diagnostic.getMessage(null)); } continue; } msgBuffer.append("\n").append(diagnostic.getMessage(null)); } manager.close(); } catch(Exception jce) { _logger.fine("cmpc.cmp_complilation_exception", jce); String msg = I18NHelper.getMessage(messages, "cmpc.cmp_complilation_exception", new Object[] {jce.getMessage()} ); GeneratorException ge = new GeneratorException(msg); ge.initCause(jce); throw ge; } if (!compilationResult) { // Log but throw an exception with a shorter message _logger.warning(I18NHelper.getMessage(messages, "cmpc.cmp_complilation_problems", msgBuffer.toString())); throw new GeneratorException(I18NHelper.getMessage( messages, "cmpc.cmp_complilation_failed")); } } /** Adds GeneratorException message to the buffer. * * @param msg the message text to add to the buffer. * @param buf the buffer to use. * @return the new or updated buffer. */ private StringBuffer addGeneratorExceptionMessage(String msg, StringBuffer buf) { StringBuffer rc = buf; if (rc == null) rc = new StringBuffer(msg); else rc.append('\n').append(msg); return rc; } // ---- VARIABLE(S) - PRIVATE -------------------------------------- private static final Logger _logger = LogHelperCmpCompiler.getLogger(); private static final ResourceBundle messages = I18NHelper.loadBundle(CMPDeployerImpl.class); }