001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.openejb.deployment;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.net.URI;
022    import java.net.URL;
023    import java.util.Collection;
024    import java.util.Collections;
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.LinkedHashSet;
028    import java.util.LinkedList;
029    import java.util.Map;
030    import java.util.Properties;
031    import java.util.Set;
032    import java.util.TreeMap;
033    import java.util.TreeSet;
034    import java.util.jar.JarFile;
035    
036    import javax.ejb.EntityContext;
037    import javax.ejb.MessageDrivenContext;
038    import javax.ejb.SessionContext;
039    import javax.xml.namespace.QName;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.geronimo.common.DeploymentException;
044    import org.apache.geronimo.connector.ResourceAdapterWrapperGBean;
045    import org.apache.geronimo.deployment.ClassPathList;
046    import org.apache.geronimo.deployment.ModuleIDBuilder;
047    import org.apache.geronimo.deployment.ModuleList;
048    import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
049    import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
050    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
051    import org.apache.geronimo.deployment.service.GBeanBuilder;
052    import org.apache.geronimo.deployment.util.DeploymentUtil;
053    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
054    import org.apache.geronimo.gbean.AbstractName;
055    import org.apache.geronimo.gbean.AbstractNameQuery;
056    import org.apache.geronimo.gbean.GBeanData;
057    import org.apache.geronimo.gbean.GBeanInfo;
058    import org.apache.geronimo.gbean.GBeanInfoBuilder;
059    import org.apache.geronimo.gbean.ReferencePatterns;
060    import org.apache.geronimo.j2ee.deployment.EARContext;
061    import org.apache.geronimo.j2ee.deployment.Module;
062    import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
063    import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
064    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
065    import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedEjbJar;
066    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
067    import org.apache.geronimo.kernel.GBeanNotFoundException;
068    import org.apache.geronimo.kernel.Naming;
069    import org.apache.geronimo.kernel.classloader.TemporaryClassLoader;
070    import org.apache.geronimo.kernel.config.Configuration;
071    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
072    import org.apache.geronimo.kernel.config.ConfigurationStore;
073    import org.apache.geronimo.kernel.repository.Artifact;
074    import org.apache.geronimo.kernel.repository.Environment;
075    import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
076    import org.apache.geronimo.openejb.EjbContainer;
077    import org.apache.geronimo.openejb.EjbDeployment;
078    import org.apache.geronimo.openejb.EjbModuleImplGBean;
079    import org.apache.geronimo.openejb.OpenEjbSystem;
080    import org.apache.geronimo.openejb.xbeans.ejbjar.OpenejbGeronimoEjbJarType;
081    import org.apache.geronimo.security.jacc.ComponentPermissions;
082    import org.apache.geronimo.xbeans.geronimo.j2ee.GerSecurityDocument;
083    import org.apache.geronimo.xbeans.javaee.EjbJarType;
084    import org.apache.openejb.OpenEJBException;
085    import org.apache.openejb.assembler.classic.AppInfo;
086    import org.apache.openejb.assembler.classic.CmpJarBuilder;
087    import org.apache.openejb.assembler.classic.ContainerInfo;
088    import org.apache.openejb.assembler.classic.ContainerSystemInfo;
089    import org.apache.openejb.assembler.classic.EjbJarInfo;
090    import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
091    import org.apache.openejb.assembler.classic.FacilitiesInfo;
092    import org.apache.openejb.assembler.classic.MdbContainerInfo;
093    import org.apache.openejb.assembler.classic.MessageDrivenBeanInfo;
094    import org.apache.openejb.assembler.classic.OpenEjbConfiguration;
095    import org.apache.openejb.config.AppModule;
096    import org.apache.openejb.config.ConfigurationFactory;
097    import org.apache.openejb.config.DeploymentLoader;
098    import org.apache.openejb.config.ReadDescriptors;
099    import org.apache.openejb.config.UnknownModuleTypeException;
100    import org.apache.openejb.config.UnsupportedModuleTypeException;
101    import org.apache.openejb.config.ValidationError;
102    import org.apache.openejb.config.ValidationFailedException;
103    import org.apache.openejb.config.ValidationFailure;
104    import org.apache.openejb.jee.EjbJar;
105    import org.apache.openejb.jee.EjbRef;
106    import org.apache.openejb.jee.EnterpriseBean;
107    import org.apache.openejb.jee.MessageDestinationRef;
108    import org.apache.openejb.jee.PersistenceContextRef;
109    import org.apache.openejb.jee.PersistenceUnitRef;
110    import org.apache.openejb.jee.ResourceEnvRef;
111    import org.apache.openejb.jee.ResourceRef;
112    import org.apache.openejb.jee.ServiceRef;
113    import org.apache.openejb.jee.jpa.unit.Persistence;
114    import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
115    import org.apache.openejb.jee.jpa.unit.TransactionType;
116    import org.apache.openejb.jee.oejb2.EjbRefType;
117    import org.apache.openejb.jee.oejb2.GeronimoEjbJarType;
118    import org.apache.openejb.jee.oejb2.MessageDrivenBeanType;
119    import org.apache.openejb.jee.oejb2.OpenejbJarType;
120    import org.apache.openejb.jee.oejb2.PatternType;
121    import org.apache.openejb.jee.oejb2.ResourceLocatorType;
122    import org.apache.openejb.loader.SystemInstance;
123    import org.apache.xmlbeans.XmlCursor;
124    import org.apache.xmlbeans.XmlObject;
125    
126    /**
127     * Master builder for processing EJB JAR deployments and creating the
128     * correspinding runtime objects (GBeans, etc.).
129     *
130     * @version $Revision: 479481 $ $Date: 2006-11-26 16:52:20 -0800 (Sun, 26 Nov 2006) $
131     */
132    public class EjbModuleBuilder implements ModuleBuilder {
133        private static final Log log = LogFactory.getLog(EjbModuleBuilder.class);
134    
135        private static final String OPENEJBJAR_NAMESPACE = XmlUtil.OPENEJBJAR_QNAME.getNamespaceURI();
136    
137        private final Environment defaultEnvironment;
138        private final String defaultCmpJTADataSource;
139        private final String defaultCmpNonJTADataSource;
140        private final NamespaceDrivenBuilderCollection securityBuilders;
141        private final NamespaceDrivenBuilderCollection serviceBuilders;
142        private final NamingBuilder namingBuilder;
143        private final ResourceEnvironmentSetter resourceEnvironmentSetter;
144        private final Collection<ModuleBuilderExtension> moduleBuilderExtensions;
145    
146        public EjbModuleBuilder(Environment defaultEnvironment,
147    
148                                String defaultCmpJTADataSource, String defaultCmpNonJTADataSource,
149                                Collection<ModuleBuilderExtension> moduleBuilderExtensions,
150                                Collection securityBuilders,
151                                Collection serviceBuilders,
152                                NamingBuilder namingBuilders,
153                                ResourceEnvironmentSetter resourceEnvironmentSetter) {
154    
155            this.defaultEnvironment = defaultEnvironment;
156            this.defaultCmpJTADataSource = defaultCmpJTADataSource;
157            this.defaultCmpNonJTADataSource = defaultCmpNonJTADataSource;
158            this.securityBuilders = new NamespaceDrivenBuilderCollection(securityBuilders, GerSecurityDocument.type.getDocumentElementName());
159            this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders, GBeanBuilder.SERVICE_QNAME);
160            this.namingBuilder = namingBuilders;
161            this.resourceEnvironmentSetter = resourceEnvironmentSetter;
162    
163            if (moduleBuilderExtensions == null) {
164                moduleBuilderExtensions = Collections.emptyList();
165            }
166            this.moduleBuilderExtensions = moduleBuilderExtensions;
167    
168            //duplicate of stuff in OpenEjbSystemGBean, may not be essential
169            System.setProperty("duct tape", "");
170            System.setProperty("admin.disabled", "true");
171            System.setProperty("openejb.logger.external", "true");
172    
173            setDefaultProperty("openejb.deploymentId.format", "{moduleId}/{ejbName}");
174            setDefaultProperty("openejb.jndiname.strategy.class", "org.apache.openejb.assembler.classic.JndiBuilder$TemplatedStrategy");
175            setDefaultProperty("openejb.jndiname.format", "{deploymentId}/{interfaceClass}");
176    
177            System.setProperty("openejb.naming", "xbean");
178    
179        }
180    
181        private void setDefaultProperty(String key, String value) {
182            SystemInstance systemInstance = SystemInstance.get();
183            String format = systemInstance.getProperty(key);
184            if (format == null) {
185                systemInstance.setProperty(key, value);
186            }
187        }
188    
189    
190        public String getSchemaNamespace() {
191            return EjbModuleBuilder.OPENEJBJAR_NAMESPACE;
192        }
193    
194        public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
195            return createModule(plan, moduleFile, "ejb.jar", null, null, null, naming, idBuilder);
196        }
197    
198        public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
199            return createModule(plan, moduleFile, targetPath, specDDUrl, environment, earName, naming, idBuilder);
200        }
201    
202        private Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment earEnvironment, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
203            if (moduleFile == null) throw new NullPointerException("moduleFile is null");
204            if (targetPath == null) throw new NullPointerException("targetPath is null");
205            if (targetPath.endsWith("/")) throw new IllegalArgumentException("targetPath must not end with a '/'");
206    
207            // Load the module file
208            DeploymentLoader loader = new DeploymentLoader();
209            AppModule appModule;
210            try {
211                appModule = loader.load(new File(moduleFile.getName()));
212            } catch (UnknownModuleTypeException e) {
213                return null;
214            } catch (UnsupportedModuleTypeException e) {
215                return null;
216            } catch (OpenEJBException e) {
217                Throwable t = e.getCause();
218                if (t instanceof UnknownModuleTypeException || t instanceof UnsupportedModuleTypeException) {
219                    return null;
220                }
221                throw new DeploymentException(e);
222            }
223    
224            // did we find a ejb jar?
225            if (appModule.getEjbModules().size() == 0) {
226                return null;
227            }
228    
229            // get the module
230            org.apache.openejb.config.EjbModule ejbModule = appModule.getEjbModules().get(0);
231    
232            // add the ejb-jar.xml altDD plan
233            if (specDDUrl != null) {
234                ejbModule.setEjbJar(null);
235                ejbModule.getAltDDs().put("ejb-jar.xml", specDDUrl);
236            }
237    
238            // convert the vendor plan object to the ejbModule altDD map
239            XmlObject unknownXmlObject = null;
240            if (plan instanceof XmlObject) {
241                unknownXmlObject = (XmlObject) plan;
242            } else if (plan != null) {
243                try {
244                    unknownXmlObject = XmlBeansUtil.parse(((File) plan).toURL(), XmlUtil.class.getClassLoader());
245                } catch (Exception e) {
246                    throw new DeploymentException(e);
247                }
248            }
249    
250            if (unknownXmlObject != null) {
251                XmlCursor xmlCursor = unknownXmlObject.newCursor();
252                //
253                QName qname = xmlCursor.getName();
254                if (qname == null) {
255                    xmlCursor.toFirstChild();
256                    qname = xmlCursor.getName();
257                }
258                if (qname.getLocalPart().equals("openejb-jar")) {
259                    ejbModule.getAltDDs().put("openejb-jar.xml", xmlCursor.xmlText());
260                } else if (qname.getLocalPart().equals("ejb-jar") && qname.getNamespaceURI().equals("http://geronimo.apache.org/xml/ns/j2ee/ejb/openejb-2.0")) {
261                    ejbModule.getAltDDs().put("geronimo-openejb.xml", xmlCursor.xmlText());
262                }
263            }
264    
265            // Read in the deploument desiptor files
266            ReadDescriptors readDescriptors = new ReadDescriptors();
267            try {
268                Thread currentThread = Thread.currentThread();
269                ClassLoader cl = currentThread.getContextClassLoader();
270                currentThread.setContextClassLoader(getClass().getClassLoader());
271                try {
272                    readDescriptors.deploy(appModule);
273                } finally {
274                    currentThread.setContextClassLoader(cl);
275                }
276            } catch (OpenEJBException e) {
277                throw new DeploymentException("Failed parsing descriptors for module: " + moduleFile.getName(), e);
278            }
279    
280            // Get the geronimo-openejb.xml tree
281            boolean standAlone = earEnvironment == null;
282            GeronimoEjbJarType geronimoEjbJarType = (GeronimoEjbJarType) ejbModule.getAltDDs().get("geronimo-openejb.xml");
283            if (geronimoEjbJarType == null) {
284                // create default plan
285                String path = (standAlone) ? new File(moduleFile.getName()).getName() : targetPath;
286                geronimoEjbJarType = XmlUtil.createDefaultPlan(path, ejbModule.getEjbJar());
287                ejbModule.getAltDDs().put("geronimo-openejb.xml", geronimoEjbJarType);
288            }
289    
290            // create the geronimo environment object
291            Environment environment = XmlUtil.buildEnvironment(geronimoEjbJarType.getEnvironment(), defaultEnvironment);
292            if (earEnvironment != null) {
293                EnvironmentBuilder.mergeEnvironments(earEnvironment, environment);
294                environment = earEnvironment;
295                if (!environment.getConfigId().isResolved()) {
296                    throw new IllegalStateException("EJB module ID should be fully resolved (not " + environment.getConfigId() + ")");
297                }
298            } else {
299                idBuilder.resolve(environment, new File(moduleFile.getName()).getName(), "jar");
300            }
301    
302    
303            AbstractName moduleName;
304            if (earName == null) {
305                earName = naming.createRootName(environment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
306                moduleName = naming.createChildName(earName, environment.getConfigId().toString(), NameFactory.EJB_MODULE);
307                ejbModule.setModuleId(environment.getConfigId().getArtifactId());
308            } else {
309                moduleName = naming.createChildName(earName, targetPath, NameFactory.EJB_MODULE);
310                ejbModule.setModuleId(targetPath);
311            }
312    
313            // Create XMLBeans version of EjbJarType for the AnnotatedApp interface
314            EjbJar ejbJar = ejbModule.getEjbJar();
315            EjbJarType ejbJarType = XmlUtil.convertToXmlbeans(ejbJar);
316            AnnotatedEjbJar annotatedEjbJar = new AnnotatedEjbJar(ejbJarType);
317    
318            EjbModule module = new EjbModule(ejbModule, standAlone, moduleName, environment, moduleFile, targetPath, "", annotatedEjbJar);
319    
320            for (ModuleBuilderExtension builder : moduleBuilderExtensions) {
321                try {
322                    builder.createModule(module, plan, moduleFile, targetPath, specDDUrl, environment, null, earName, naming, idBuilder);
323                } catch (Throwable t) {
324                    String builderName = builder.getClass().getSimpleName();
325                    log.error(builderName + ".createModule() failed: " + t.getMessage(), t);
326    
327                }
328            }
329            return module;
330        }
331    
332        protected static void unmapReferences(EjbJar ejbJar, GeronimoEjbJarType geronimoEjbJarType) {
333            Set<String> corbaEjbRefs = new TreeSet<String>();
334            for (EjbRefType ejbRef : geronimoEjbJarType.getEjbRef()) {
335                if (ejbRef.getNsCorbaloc() != null) {
336                    corbaEjbRefs.add(ejbRef.getRefName());
337                }
338            }
339    
340            for (EnterpriseBean enterpriseBean : ejbJar.getEnterpriseBeans()) {
341                enterpriseBean.getEnvEntry().clear();
342                enterpriseBean.getEjbLocalRef().clear();
343    
344                for (Iterator<EjbRef> iterator = enterpriseBean.getEjbRef().iterator(); iterator.hasNext();) {
345                    EjbRef ref = iterator.next();
346                    if (!corbaEjbRefs.contains(ref.getEjbRefName())) {
347                        // remove all non corba refs to avoid overwriting normal ejb refs
348                        iterator.remove();
349                    } else {
350                        // clear mapped named data from corba refs
351                        ref.setMappedName(null);
352                        ref.getInjectionTarget().clear();
353                    }
354                }
355    
356                for (MessageDestinationRef ref : enterpriseBean.getMessageDestinationRef()) {
357                    ref.setMappedName(null);
358                    ref.getInjectionTarget().clear();
359                }
360                for (PersistenceContextRef ref : enterpriseBean.getPersistenceContextRef()) {
361                    ref.setMappedName(null);
362                    ref.getInjectionTarget().clear();
363                }
364                for (PersistenceUnitRef ref : enterpriseBean.getPersistenceUnitRef()) {
365                    ref.setMappedName(null);
366                    ref.getInjectionTarget().clear();
367                }
368                for (ResourceRef ref : enterpriseBean.getResourceRef()) {
369                    ref.setMappedName(null);
370                    ref.getInjectionTarget().clear();
371                }
372                for (Iterator<ResourceEnvRef> iterator = enterpriseBean.getResourceEnvRef().iterator(); iterator.hasNext();) {
373                    ResourceEnvRef ref = iterator.next();
374                    if (ref.getType().equals(SessionContext.class.getName())) {
375                        iterator.remove();
376                    } else if (ref.getType().equals(EntityContext.class.getName())) {
377                        iterator.remove();
378                    } else if (ref.getType().equals(MessageDrivenContext.class.getName())) {
379                        iterator.remove();
380                    } else {
381                        ref.setMappedName(null);
382                    }
383                    ref.getInjectionTarget().clear();
384    
385                }
386                for (ServiceRef ref : enterpriseBean.getServiceRef()) {
387                    ref.setMappedName(null);
388                    ref.getInjectionTarget().clear();
389                }
390            }
391        }
392    
393    
394        public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repository) throws DeploymentException {
395            installModule(module, earContext);
396            EARContext moduleContext;
397            if (module.isStandAlone()) {
398                moduleContext = earContext;
399            } else {
400                Environment environment = earContext.getConfiguration().getEnvironment();
401                File configurationDir = new File(earContext.getBaseDir(), module.getTargetPath());
402    //            configurationDir.mkdirs();
403    
404                // construct the ejb app deployment context... this is the same class used by the ear context
405                try {
406                    File inPlaceConfigurationDir = null;
407                    if (null != earContext.getInPlaceConfigurationDir()) {
408                        inPlaceConfigurationDir = new File(earContext.getInPlaceConfigurationDir(), module.getTargetPath());
409                    }
410                    moduleContext = new EARContext(configurationDir,
411                            inPlaceConfigurationDir,
412                            environment,
413                            ConfigurationModuleType.EJB,
414                            module.getModuleName(),
415                            earContext);
416                } catch (DeploymentException e) {
417                    cleanupConfigurationDir(configurationDir);
418                    throw e;
419                }
420            }
421            module.setEarContext(moduleContext);
422            module.setRootEarContext(earContext);
423            if (((EjbModule) module).getEjbJar().getAssemblyDescriptor() != null) {
424                namingBuilder.buildEnvironment(null, null, module.getEnvironment());
425            }
426            for (ModuleBuilderExtension builder : moduleBuilderExtensions) {
427                try {
428                    builder.installModule(earFile, earContext, module, configurationStores, targetConfigurationStore, repository);
429                } catch (Throwable t) {
430                    String builderName = builder.getClass().getSimpleName();
431                    log.error(builderName + ".installModule() failed: " + t.getMessage(), t);
432                }
433            }
434        }
435    
436        private void installModule(Module module, EARContext earContext) throws DeploymentException {
437            EarData earData = (EarData) earContext.getGeneralData().get(EarData.class);
438            if (earData == null) {
439                earData = new EarData();
440                earContext.getGeneralData().put(EarData.class, earData);
441            }
442            earData.addEjbModule((EjbModule) module);
443    
444            JarFile moduleFile = module.getModuleFile();
445            try {
446                // extract the ejbJar file into a standalone packed jar file and add the contents to the output
447                earContext.addIncludeAsPackedJar(URI.create(module.getTargetPath()), moduleFile);
448            } catch (IOException e) {
449                throw new DeploymentException("Unable to copy ejb module jar into configuration: " + moduleFile.getName(), e);
450            }
451        }
452    
453        private static final String LINE_SEP = System.getProperty("line.separator");
454    
455        private boolean cleanupConfigurationDir(File configurationDir) {
456            LinkedList<String> cannotBeDeletedList = new LinkedList<String>();
457    
458            if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) {
459                // Output a message to help user track down file problem
460                log.warn("Unable to delete " + cannotBeDeletedList.size() +
461                        " files while recursively deleting directory "
462                        + configurationDir.getAbsolutePath() + LINE_SEP +
463                        "The first file that could not be deleted was:" + LINE_SEP + "  " +
464                        (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : ""));
465                return false;
466            }
467            return true;
468        }
469    
470        public void initContext(EARContext earContext, Module module, ClassLoader classLoader) throws DeploymentException {
471            EjbModule ejbModule = (EjbModule) module;
472    
473            EjbJarInfo ejbJarInfo = getEjbJarInfo(earContext, ejbModule, classLoader);
474    
475            ejbModule.setEjbJarInfo(ejbJarInfo);
476    
477            // update the original spec dd with the metadata complete dd
478            EjbJar ejbJar = ejbModule.getEjbJar();
479            ejbModule.setOriginalSpecDD(XmlUtil.marshal(ejbModule.getEjbJar()));
480    
481            // Get the geronimo-openejb plan
482            GeronimoEjbJarType geronimoEjbJarType = (GeronimoEjbJarType) ejbModule.getEjbModule().getAltDDs().get("geronimo-openejb.xml");
483    
484            // We must set all mapped name references back to null or Geronimo will blow up
485            unmapReferences(ejbJar, geronimoEjbJarType);
486    
487            // create a xmlbeans version of the ejb-jar.xml file, because the jndi code is coupled based on xmlbeans objects
488            EjbJarType ejbJarType = XmlUtil.convertToXmlbeans(ejbJar);
489            ejbModule.setSpecDD(ejbJarType);
490    
491            // convert the plan to xmlbeans since geronimo naming is coupled on xmlbeans objects
492            OpenejbGeronimoEjbJarType geronimoOpenejb = XmlUtil.convertToXmlbeans(geronimoEjbJarType);
493            ejbModule.setVendorDD(geronimoOpenejb);
494    
495            // todo move namingBuilders.buildEnvironment() here when geronimo naming supports it
496    
497            // initialize the naming builders
498            if (ejbJarType.getAssemblyDescriptor() != null) {
499                namingBuilder.initContext(ejbJarType.getAssemblyDescriptor(),
500                        geronimoOpenejb,
501                        ejbModule);
502            }
503    
504            EjbDeploymentBuilder ejbDeploymentBuilder = new EjbDeploymentBuilder(earContext, ejbModule, namingBuilder, resourceEnvironmentSetter);
505            ejbModule.setEjbBuilder(ejbDeploymentBuilder);
506            ejbDeploymentBuilder.initContext();
507    
508            // Build the security configuration.
509            securityBuilders.build(geronimoOpenejb, earContext, ejbModule.isStandAlone() ? ejbModule.getEarContext() : null);
510    
511            // Add extra gbean declared in the geronimo-openejb.xml file
512            serviceBuilders.build(geronimoOpenejb, earContext, ejbModule.getEarContext());
513    
514            ClassPathList manifestcp = new ClassPathList();
515            manifestcp.add(module.getTargetPath());
516            EARContext moduleContext = module.getEarContext();
517            ModuleList moduleLocations = (ModuleList) module.getRootEarContext().getGeneralData().get(ModuleList.class);
518            URI baseUri = URI.create(module.getTargetPath());
519            moduleContext.getCompleteManifestClassPath(module.getModuleFile(), baseUri, URI.create("."), manifestcp, moduleLocations);
520            moduleContext.getGeneralData().put(ClassPathList.class, manifestcp);
521    
522            for (ModuleBuilderExtension builder : moduleBuilderExtensions) {
523                try {
524                    builder.initContext(earContext, module, classLoader);
525                } catch (Throwable t) {
526                    String builderName = builder.getClass().getSimpleName();
527                    log.error(builderName + ".initContext() failed: " + t.getMessage(), t);
528                }
529            }
530        }
531    
532        private EjbJarInfo getEjbJarInfo(EARContext earContext, EjbModule ejbModule, ClassLoader classLoader) throws DeploymentException {
533            EarData earData = (EarData) earContext.getGeneralData().get(EarData.class);
534            if (earData.getEjbJars().isEmpty()) {
535    
536                // temporary classloader is used for processing ejb annotations and byte code manipulation during ejb load
537                TemporaryClassLoader temporaryClassLoader = new TemporaryClassLoader(new URL[0], classLoader);
538    
539                // create an openejb app module for the ear containing all ejb modules
540                AppModule appModule = new AppModule(classLoader, earContext.getConfigID().toString());
541                for (EjbModule module : earData.getEjbModuels()) {
542                    module.setClassLoader(temporaryClassLoader);
543                    appModule.getEjbModules().add(module.getEjbModule());
544                }
545    
546                // build the config info tree
547                // this method fills in the ejbJar jaxb tree based on the annotations
548                // (metadata complete) and it run the openejb verifier
549                AppInfo appInfo;
550                try {
551                    appInfo = configureApplication(appModule, earContext.getConfiguration());
552                } catch (ValidationFailedException set) {
553                    StringBuilder sb = new StringBuilder();
554                    sb.append("Jar failed validation: ").append(appModule.getModuleId());
555    
556                    for (ValidationError e : set.getErrors()) {
557                        sb.append(e.getPrefix()).append(" ... ").append(e.getComponentName()).append(":\t").append(e.getMessage(2));
558                    }
559    
560                    for (ValidationFailure e : set.getFailures()) {
561                        sb.append(e.getPrefix()).append(" ... ").append(e.getComponentName()).append(":\t").append(e.getMessage(2));
562                    }
563    
564                    throw new DeploymentException(sb.toString());
565                } catch (OpenEJBException e) {
566                    throw new DeploymentException(e);
567                }
568    
569                // add all of the modules to the ear data
570                for (EjbJarInfo ejbJar : appInfo.ejbJars) {
571                    earData.addEjbJar(ejbJar);
572                }
573    
574                // add the cmp jar
575                CmpJarBuilder cmp2Builder = new CmpJarBuilder(appInfo, classLoader);
576                try {
577                    File generatedJar = cmp2Builder.getJarFile();
578                    if (generatedJar != null) {
579                        String generatedPath = ejbModule.getTargetPath();
580                        if (generatedPath.endsWith(".jar")) {
581                            generatedPath = generatedPath.substring(0, generatedPath.length() - 4);
582                        }
583                        generatedPath += "-cmp2.jar";
584                        earContext.addInclude(URI.create(generatedPath), generatedJar);
585                    }
586                } catch (IOException e) {
587                    throw new DeploymentException(e);
588                }
589    
590                // add the cmp persistence unit if needed
591                if (appInfo.cmpMappingsXml != null) {
592                    addGeronimmoOpenEJBPersistenceUnit(ejbModule);
593                }
594            }
595    
596            // find our module
597            EjbJarInfo ejbJarInfo = earData.getEjbJar(ejbModule.getEjbModule().getModuleId());
598            return ejbJarInfo;
599        }
600    
601        private AppInfo configureApplication(AppModule appModule, Configuration configuration) throws OpenEJBException {
602            OpenEjbConfiguration openEjbConfiguration = new OpenEjbConfiguration();
603            openEjbConfiguration.containerSystem = new ContainerSystemInfo();
604            openEjbConfiguration.facilities = new FacilitiesInfo();
605            boolean offline = true;
606            ConfigurationFactory configurationFactory = new ConfigurationFactory(offline, openEjbConfiguration);
607            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
608            Thread.currentThread().setContextClassLoader(appModule.getClassLoader());
609            try {
610                addContainerInfos(configuration, openEjbConfiguration.containerSystem, configurationFactory);
611                addResourceAdapterMDBInfos(configuration, openEjbConfiguration.containerSystem, configurationFactory);
612                //process resource adapters
613    
614                return configurationFactory.configureApplication(appModule);
615            } finally {
616                Thread.currentThread().setContextClassLoader(oldClassLoader);
617            }
618        }
619    
620        private void addContainerInfos(Configuration configuration, ContainerSystemInfo containerSystem, ConfigurationFactory configurationFactory) throws OpenEJBException {
621            LinkedHashSet<GBeanData> containerDatas = configuration.findGBeanDatas(Collections.singleton(new AbstractNameQuery(EjbContainer.class.getName())));
622            for (GBeanData containerData : containerDatas) {
623                Class<? extends ContainerInfo> infoClass = (Class<? extends ContainerInfo>) containerData.getAttribute("infoType");
624                if (infoClass == null) {
625                    String type = (String) containerData.getAttribute("type");
626                    infoClass = EjbContainer.getInfoType(type);
627                }
628                String serviceId = (String) containerData.getAttribute("id");
629                Properties declaredProperties = (Properties) containerData.getAttribute("properties");
630                String providerId = (String) containerData.getAttribute("provider");
631                ContainerInfo containerInfo = configurationFactory.configureService(infoClass, serviceId, declaredProperties, providerId, "Container");
632                containerSystem.containers.add(containerInfo);
633            }
634        }
635    
636        private void addResourceAdapterMDBInfos(Configuration configuration, ContainerSystemInfo containerSystem, ConfigurationFactory configurationFactory) throws OpenEJBException {
637            LinkedHashSet<GBeanData> resourceAdapterWrappers = configuration.findGBeanDatas(Collections.singleton(new AbstractNameQuery(ResourceAdapterWrapperGBean.class.getName())));
638            for (GBeanData resourceAdapterWrapperData : resourceAdapterWrappers) {
639                String resourceAdapterId = getResourceAdapterId(resourceAdapterWrapperData.getAbstractName());
640                Map<String, String> messageListenerToActivationSpecMap = (Map<String, String>) resourceAdapterWrapperData.getAttribute("messageListenerToActivationSpecMap");
641                if (messageListenerToActivationSpecMap == null) {
642                    continue;
643                }
644                for (Map.Entry<String, String> entry : messageListenerToActivationSpecMap.entrySet()) {
645                    String messageListenerInterface = entry.getKey();
646                    String activationSpecClass = entry.getValue();
647    
648                    // only process RA if not previously processed
649                    String containerName = resourceAdapterId + "-" + messageListenerInterface;
650                    // get default mdb config
651                    ContainerInfo containerInfo = configurationFactory.configureService(MdbContainerInfo.class);
652                    containerInfo.id = containerName;
653                    containerInfo.displayName = containerName;
654    
655                    // set ra specific properties
656    
657                    try {
658                        containerInfo.properties.put("MessageListenerInterface",
659                            configuration.getConfigurationClassLoader().loadClass(messageListenerInterface));
660                    } catch (ClassNotFoundException e) {
661                        throw new OpenEJBException("Could not load MessageListenerInterface " + messageListenerInterface + " in classloader: " + configuration.getConfigurationClassLoader(), e);
662                    }
663                    try {
664                        containerInfo.properties.put("ActivationSpecClass",
665                            configuration.getConfigurationClassLoader().loadClass(activationSpecClass));
666                    } catch (ClassNotFoundException e) {
667                        throw new OpenEJBException("Could not load ActivationSpecClass " + activationSpecClass + " in classloader: " + configuration.getConfigurationClassLoader(), e);
668                    }
669                    //TODO is this necessary????
670    //                containerInfo.properties.put("ResourceAdapter", resourceAdapter);
671    
672                    containerSystem.containers.add(containerInfo);
673                }
674            }
675        }
676    
677    
678        private void addGeronimmoOpenEJBPersistenceUnit(EjbModule ejbModule) {
679            GeronimoEjbJarType geronimoEjbJarType = (GeronimoEjbJarType) ejbModule.getEjbModule().getAltDDs().get("geronimo-openejb.xml");
680    
681            // search for the cmp persistence unit
682            PersistenceUnit persistenceUnit = null;
683            for (Persistence persistence : geronimoEjbJarType.getPersistence()) {
684                for (PersistenceUnit unit : persistence.getPersistenceUnit()) {
685                    if ("cmp".equals(unit.getName())) {
686                        persistenceUnit = unit;
687                        break;
688                    }
689                }
690            }
691    
692            // if not found create one
693            if (persistenceUnit == null) {
694                String jtaDataSource = null;
695                // todo Persistence Unit Data Sources need to be global JNDI names
696                Object altDD = ejbModule.getEjbModule().getAltDDs().get("openejb-jar.xml");
697                if (altDD instanceof OpenejbJarType) {
698                    ResourceLocatorType cmpConnectionFactory = ((OpenejbJarType) altDD).getCmpConnectionFactory();
699                    if (cmpConnectionFactory != null) {
700                        String datasourceName = cmpConnectionFactory.getResourceLink();
701                        if (datasourceName != null) {
702                            jtaDataSource = datasourceName.trim();
703                        }
704                    }
705                }
706    
707                persistenceUnit = new PersistenceUnit();
708                persistenceUnit.setName("cmp");
709                persistenceUnit.setTransactionType(TransactionType.JTA);
710                if (jtaDataSource != null) {
711                    persistenceUnit.setJtaDataSource(jtaDataSource);
712                } else {
713                    persistenceUnit.setJtaDataSource(defaultCmpJTADataSource);
714                }
715                persistenceUnit.setNonJtaDataSource(defaultCmpNonJTADataSource);
716                persistenceUnit.setExcludeUnlistedClasses(true);
717    
718                Persistence persistence = new Persistence();
719                persistence.setVersion("1.0");
720                persistence.getPersistenceUnit().add(persistenceUnit);
721    
722                geronimoEjbJarType.getPersistence().add(persistence);
723            }
724            persistenceUnit.getMappingFile().add("META-INF/openejb-cmp-generated-orm.xml");
725        }
726    
727        /**
728         * Does the meaty work of processing the deployment information and
729         * creating GBeans for all the EJBs in the JAR, etc.
730         */
731        public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repositories) throws DeploymentException {
732            EjbModule ejbModule = (EjbModule) module;
733            EjbDeploymentBuilder ejbDeploymentBuilder = ejbModule.getEjbBuilder();
734    
735            // add enc
736            ejbDeploymentBuilder.buildEnc();
737    
738            // Add JSR77 EJBModule GBean
739            GBeanData ejbModuleGBeanData = new GBeanData(ejbModule.getModuleName(), EjbModuleImplGBean.GBEAN_INFO);
740            try {
741                ejbModuleGBeanData.setReferencePattern("J2EEServer", earContext.getServerName());
742                if (!ejbModule.isStandAlone()) {
743                    ejbModuleGBeanData.setReferencePattern("J2EEApplication", earContext.getModuleName());
744                }
745    
746                ejbModuleGBeanData.setAttribute("deploymentDescriptor", ejbModule.getOriginalSpecDD());
747    
748                ejbModuleGBeanData.setReferencePatterns("EJBCollection",
749                        new ReferencePatterns(new AbstractNameQuery(null,
750                                Collections.singletonMap(NameFactory.EJB_MODULE, ejbModule.getModuleName().getNameProperty(NameFactory.J2EE_NAME)),
751                                EjbDeployment.class.getName())));
752    
753                ejbModuleGBeanData.setReferencePattern("OpenEjbSystem", new AbstractNameQuery(null, Collections.EMPTY_MAP, OpenEjbSystem.class.getName()));
754                ejbModuleGBeanData.setAttribute("ejbJarInfo", ejbModule.getEjbJarInfo());
755    
756                earContext.addGBean(ejbModuleGBeanData);
757            } catch (Exception e) {
758                throw new DeploymentException("Unable to initialize EJBModule GBean " + ejbModuleGBeanData.getAbstractName(), e);
759            }
760    
761            // add a depdendency on the ejb module object
762            ejbDeploymentBuilder.addEjbModuleDependency(ejbModuleGBeanData.getAbstractName());
763    
764            // add the Jacc permissions to the ear
765            ComponentPermissions componentPermissions = ejbDeploymentBuilder.buildComponentPermissions();
766            earContext.addSecurityContext(ejbModule.getEjbJarInfo().moduleId, componentPermissions);
767    
768            setMdbContainerIds(earContext, ejbModule, ejbModuleGBeanData);
769    
770            for (ModuleBuilderExtension builder : moduleBuilderExtensions) {
771                try {
772                    builder.addGBeans(earContext, module, cl, repositories);
773                } catch (Throwable t) {
774                    String builderName = builder.getClass().getSimpleName();
775                    log.error(builderName + ".addGBeans() failed: " + t.getMessage(), t);
776                }
777            }
778        }
779    
780        private void setMdbContainerIds(EARContext earContext, EjbModule ejbModule, GBeanData ejbModuleGBeanData) throws DeploymentException {
781            Object altDD = ejbModule.getEjbModule().getAltDDs().get("openejb-jar.xml");
782            if (!(altDD instanceof OpenejbJarType)) {
783                return;
784            }
785            OpenejbJarType openejbJarType = (OpenejbJarType) altDD;
786            EjbJarInfo ejbJarInfo = ejbModule.getEjbJarInfo();
787    
788            Map<String, MessageDrivenBeanInfo> mdbs = new TreeMap<String, MessageDrivenBeanInfo>();
789            for (EnterpriseBeanInfo enterpriseBean : ejbJarInfo.enterpriseBeans) {
790                if (enterpriseBean instanceof MessageDrivenBeanInfo) {
791                    mdbs.put(enterpriseBean.ejbName, (MessageDrivenBeanInfo) enterpriseBean);
792                }
793            }
794            for (org.apache.openejb.jee.oejb2.EnterpriseBean enterpriseBean : openejbJarType.getEnterpriseBeans()) {
795                if (!(enterpriseBean instanceof MessageDrivenBeanType)) {
796                    continue;
797                }
798                MessageDrivenBeanType bean = (MessageDrivenBeanType) enterpriseBean;
799                MessageDrivenBeanInfo messageDrivenBeanInfo = mdbs.get(bean.getEjbName());
800                if (messageDrivenBeanInfo == null) {
801                    continue;
802                }
803                if (messageDrivenBeanInfo.containerId != null) {
804                    // containerId already set
805                    continue;
806                }
807    
808                if (bean.getResourceAdapter() == null) {
809                    throw new DeploymentException("No Resource Adapter defined for MDB '" + bean.getEjbName() + "'");
810                }
811    
812                AbstractNameQuery resourceAdapterNameQuery = getResourceAdapterNameQuery(bean.getResourceAdapter());
813                AbstractName resourceAdapterAbstractName;
814                try {
815                    resourceAdapterAbstractName = earContext.findGBean(resourceAdapterNameQuery);
816                } catch (GBeanNotFoundException e) {
817                    throw new DeploymentException("Resource Adapter for MDB '" + bean.getEjbName() + "'not found: " + resourceAdapterNameQuery, e);
818                }
819    
820                String resourceAdapterId = getResourceAdapterId(resourceAdapterAbstractName);
821                messageDrivenBeanInfo.containerId = resourceAdapterId + "-" + messageDrivenBeanInfo.mdbInterface;
822    
823                // add a dependency from the module to the ra so we can be assured the mdb
824                // container exists when this app is started
825                ejbModuleGBeanData.addDependency(resourceAdapterAbstractName);
826            }
827        }
828    
829        private String getResourceAdapterId(AbstractName resourceAdapterAbstractName) {
830            Map properties = resourceAdapterAbstractName.getName();
831            String shortName = (String) properties.get("name");
832            String moduleName = (String) properties.get("ResourceAdapterModule");
833            if (shortName != null && moduleName != null) {
834                return moduleName + "." + shortName;
835            } else {
836                return resourceAdapterAbstractName.getObjectName().toString();
837            }
838        }
839    
840        private static AbstractNameQuery getResourceAdapterNameQuery(ResourceLocatorType resourceLocator) {
841            if (resourceLocator.getResourceLink() != null) {
842                Map<String, String> nameMap = new HashMap<String, String>();
843                nameMap.put("name", resourceLocator.getResourceLink());
844                nameMap.put("j2eeType", NameFactory.JCA_RESOURCE_ADAPTER);
845                return new AbstractNameQuery(null, nameMap);
846            }
847    
848            //construct name from components
849            PatternType pattern = resourceLocator.getPattern();
850            Artifact artifact = null;
851            if (pattern.getArtifactId() != null) {
852                artifact = new Artifact(pattern.getGroupId(), pattern.getArtifactId(), pattern.getVersion(), "car");
853            }
854    
855            Map<String, String> nameMap = new HashMap<String, String>();
856            nameMap.put("name", pattern.getName());
857            nameMap.put("j2eeType", NameFactory.JCA_RESOURCE_ADAPTER);
858            if (pattern.getModule() != null) {
859                nameMap.put(NameFactory.RESOURCE_ADAPTER_MODULE, pattern.getModule());
860            }
861            return new AbstractNameQuery(artifact, nameMap, (Set) null);
862        }
863    
864        public static class EarData {
865            private final Map<String, EjbModule> ejbModules = new TreeMap<String, EjbModule>();
866            private final Map<String, EjbJarInfo> ejbJars = new TreeMap<String, EjbJarInfo>();
867    
868            public void addEjbModule(EjbModule ejbModule) {
869                ejbModules.put(ejbModule.getEjbModule().getModuleId(), ejbModule);
870            }
871    
872            public EjbModule getEjbModule(String moduleId) throws DeploymentException {
873                EjbModule ejbModule = ejbModules.get(moduleId);
874                if (ejbModule == null) {
875                    throw new DeploymentException("Ejb  module " + moduleId + " was not found in configured module list " + ejbModules.keySet());
876                }
877                return ejbModule;
878            }
879    
880            public Collection<EjbModule> getEjbModuels() {
881                return ejbModules.values();
882            }
883    
884            public void addEjbJar(EjbJarInfo ejbJarInfo) {
885                ejbJars.put(ejbJarInfo.moduleId, ejbJarInfo);
886            }
887    
888            public EjbJarInfo getEjbJar(String moduleId) throws DeploymentException {
889                EjbJarInfo ejbJarInfo = ejbJars.get(moduleId);
890                if (ejbJarInfo == null) {
891                    throw new DeploymentException("Ejb jar configuration passed but expected module " +
892                            moduleId + " was not found in configured module list " + ejbJars.keySet());
893                }
894                return ejbJarInfo;
895            }
896    
897            public Collection<EjbJarInfo> getEjbJars() {
898                return ejbJars.values();
899            }
900        }
901    
902    
903        public static final GBeanInfo GBEAN_INFO;
904    
905        static {
906            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(EjbModuleBuilder.class, NameFactory.MODULE_BUILDER);
907            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true);
908            infoBuilder.addAttribute("defaultCmpJTADataSource", String.class, true);
909            infoBuilder.addAttribute("defaultCmpNonJTADataSource", String.class, true);
910            infoBuilder.addReference("ModuleBuilderExtensions", ModuleBuilderExtension.class, NameFactory.MODULE_BUILDER);
911            infoBuilder.addReference("SecurityBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
912            infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
913            infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
914            infoBuilder.addReference("ResourceEnvironmentSetter", ResourceEnvironmentSetter.class, NameFactory.MODULE_BUILDER);
915    
916            infoBuilder.setConstructor(new String[]{
917                    "defaultEnvironment",
918                    "defaultCmpJTADataSource",
919                    "defaultCmpNonJTADataSource",
920                    "ModuleBuilderExtensions",
921                    "SecurityBuilders",
922                    "ServiceBuilders",
923                    "NamingBuilders",
924                    "ResourceEnvironmentSetter"});
925            GBEAN_INFO = infoBuilder.getBeanInfo();
926        }
927    
928        public static GBeanInfo getGBeanInfo() {
929            return GBEAN_INFO;
930        }
931    
932    }