001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.client.builder;
019    
020    import java.io.File;
021    import java.io.IOException;
022    import java.net.URI;
023    import java.net.URISyntaxException;
024    import java.net.URL;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.LinkedList;
030    import java.util.Map;
031    import java.util.StringTokenizer;
032    import java.util.HashMap;
033    import java.util.jar.Attributes;
034    import java.util.jar.JarFile;
035    import java.util.jar.Manifest;
036    import java.util.zip.ZipEntry;
037    
038    import org.apache.commons.logging.Log;
039    import org.apache.commons.logging.LogFactory;
040    import org.apache.geronimo.client.AppClientContainer;
041    import org.apache.geronimo.client.StaticJndiContextPlugin;
042    import org.apache.geronimo.common.DeploymentException;
043    import org.apache.geronimo.deployment.DeploymentContext;
044    import org.apache.geronimo.deployment.ModuleIDBuilder;
045    import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
046    import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
047    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
048    import org.apache.geronimo.deployment.util.DeploymentUtil;
049    import org.apache.geronimo.deployment.util.NestedJarFile;
050    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
051    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
052    import org.apache.geronimo.gbean.AbstractName;
053    import org.apache.geronimo.gbean.AbstractNameQuery;
054    import org.apache.geronimo.gbean.GBeanData;
055    import org.apache.geronimo.gbean.GBeanInfo;
056    import org.apache.geronimo.gbean.GBeanInfoBuilder;
057    import org.apache.geronimo.gbean.SingleElementCollection;
058    import org.apache.geronimo.j2ee.deployment.AppClientModule;
059    import org.apache.geronimo.j2ee.deployment.EARContext;
060    import org.apache.geronimo.j2ee.deployment.Module;
061    import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
062    import org.apache.geronimo.j2ee.deployment.SecurityBuilder;
063    import org.apache.geronimo.j2ee.deployment.CorbaGBeanNameSource;
064    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
065    import org.apache.geronimo.j2ee.deployment.NamingBuilderCollection;
066    import org.apache.geronimo.j2ee.deployment.ConnectorModule;
067    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
068    import org.apache.geronimo.j2ee.management.impl.J2EEAppClientModuleImpl;
069    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
070    import org.apache.geronimo.kernel.Naming;
071    import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
072    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
073    import org.apache.geronimo.kernel.config.ConfigurationStore;
074    import org.apache.geronimo.kernel.config.Configuration;
075    import org.apache.geronimo.kernel.repository.Artifact;
076    import org.apache.geronimo.kernel.repository.Environment;
077    import org.apache.geronimo.kernel.repository.Repository;
078    import org.apache.geronimo.schema.SchemaConversionUtils;
079    import org.apache.geronimo.security.deploy.DefaultPrincipal;
080    import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientDocument;
081    import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientType;
082    import org.apache.geronimo.xbeans.geronimo.client.GerResourceType;
083    import org.apache.geronimo.xbeans.geronimo.naming.GerAbstractNamingEntryDocument;
084    import org.apache.geronimo.xbeans.j2ee.ApplicationClientDocument;
085    import org.apache.geronimo.xbeans.j2ee.ApplicationClientType;
086    import org.apache.xmlbeans.XmlException;
087    import org.apache.xmlbeans.XmlObject;
088    import org.apache.xmlbeans.XmlCursor;
089    
090    
091    /**
092     * @version $Rev:385232 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
093     */
094    public class AppClientModuleBuilder implements ModuleBuilder, CorbaGBeanNameSource {
095        private static final Log log = LogFactory.getLog(AppClientModuleBuilder.class);
096        private static final String LINE_SEP = System.getProperty("line.separator");
097    
098        private final Environment defaultClientEnvironment;
099        private final Environment defaultServerEnvironment;
100        private final AbstractNameQuery corbaGBeanObjectName;
101    
102        private final AbstractNameQuery transactionManagerObjectName;
103        private final AbstractNameQuery connectionTrackerObjectName;
104        private final SingleElementCollection connectorModuleBuilder;
105        private final SingleElementCollection securityBuilder;
106        private final NamespaceDrivenBuilderCollection serviceBuilder;
107        private final NamingBuilderCollection namingBuilders;
108    
109        private final Collection repositories;
110    
111        private static final String GERAPPCLIENT_NAMESPACE = GerApplicationClientDocument.type.getDocumentElementName().getNamespaceURI();
112    
113        public AppClientModuleBuilder(Environment defaultClientEnvironment,
114                Environment defaultServerEnvironment,
115                AbstractNameQuery transactionManagerObjectName,
116                AbstractNameQuery connectionTrackerObjectName,
117                AbstractNameQuery corbaGBeanObjectName,
118                Collection repositories,
119                ModuleBuilder connectorModuleBuilder,
120                NamespaceDrivenBuilder securityBuilder,
121                NamespaceDrivenBuilder serviceBuilder,
122                Collection namingBuilders) {
123            this(defaultClientEnvironment,
124                    defaultServerEnvironment,
125                    transactionManagerObjectName,
126                    connectionTrackerObjectName,
127                    corbaGBeanObjectName,
128                    repositories, new SingleElementCollection(connectorModuleBuilder),
129                    new SingleElementCollection(securityBuilder),
130                    serviceBuilder == null ? Collections.EMPTY_SET : Collections.singleton(serviceBuilder),
131                    namingBuilders == null ? Collections.EMPTY_SET : namingBuilders);
132        }
133    
134        public AppClientModuleBuilder(AbstractNameQuery transactionManagerObjectName,
135                AbstractNameQuery connectionTrackerObjectName,
136                AbstractNameQuery corbaGBeanObjectName,
137                Collection repositories,
138                Collection connectorModuleBuilder,
139                Collection securityBuilder,
140                Collection serviceBuilder,
141                Collection namingBuilders,
142                Environment defaultClientEnvironment,
143                Environment defaultServerEnvironment) {
144            this(defaultClientEnvironment,
145                    defaultServerEnvironment,
146                    transactionManagerObjectName,
147                    connectionTrackerObjectName,
148                    corbaGBeanObjectName,
149                    repositories, new SingleElementCollection(connectorModuleBuilder),
150                    new SingleElementCollection(securityBuilder),
151                    serviceBuilder,
152                    namingBuilders);
153        }
154    
155        private AppClientModuleBuilder(Environment defaultClientEnvironment,
156                Environment defaultServerEnvironment,
157                AbstractNameQuery transactionManagerObjectName,
158                AbstractNameQuery connectionTrackerObjectName,
159                AbstractNameQuery corbaGBeanObjectName,
160                Collection repositories,
161                SingleElementCollection connectorModuleBuilder,
162                SingleElementCollection securityBuilder,
163                Collection serviceBuilder, Collection namingBuilders) {
164            this.defaultClientEnvironment = defaultClientEnvironment;
165            this.defaultServerEnvironment = defaultServerEnvironment;
166            this.corbaGBeanObjectName = corbaGBeanObjectName;
167            this.transactionManagerObjectName = transactionManagerObjectName;
168            this.connectionTrackerObjectName = connectionTrackerObjectName;
169            this.repositories = repositories;
170            this.connectorModuleBuilder = connectorModuleBuilder;
171            this.securityBuilder = securityBuilder;
172            this.serviceBuilder = new NamespaceDrivenBuilderCollection(serviceBuilder);
173            this.namingBuilders = new NamingBuilderCollection(namingBuilders, GerAbstractNamingEntryDocument.type.getDocumentElementName());
174        }
175    
176    
177        public AbstractNameQuery getCorbaGBeanName() {
178            return corbaGBeanObjectName;
179        }
180    
181        private ModuleBuilder getConnectorModuleBuilder() {
182            return (ModuleBuilder) connectorModuleBuilder.getElement();
183        }
184    
185        private org.apache.geronimo.j2ee.deployment.SecurityBuilder getSecurityBuilder() {
186            return (SecurityBuilder) securityBuilder.getElement();
187        }
188    
189        public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
190            return createModule(plan, moduleFile, "app-client", null, null, null, naming, idBuilder);
191        }
192    
193        public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
194            return createModule(plan, moduleFile, targetPath, specDDUrl, environment, earName, naming, idBuilder);
195        }
196    
197        private Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment earEnvironment, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
198            assert moduleFile != null: "moduleFile is null";
199            assert targetPath != null: "targetPath is null";
200            assert !targetPath.endsWith("/"): "targetPath must not end with a '/'";
201            assert (earName == null) == (earEnvironment == null): "if earName is not null you must supply earEnvironment as well";
202    
203            String specDD;
204            ApplicationClientType appClient;
205            try {
206                if (specDDUrl == null) {
207                    specDDUrl = DeploymentUtil.createJarURL(moduleFile, "META-INF/application-client.xml");
208                }
209    
210                // read in the entire specDD as a string, we need this for getDeploymentDescriptor
211                // on the J2ee management object
212                specDD = DeploymentUtil.readAll(specDDUrl);
213            } catch (Exception e) {
214                //no application-client.xml, not for us.
215                return null;
216            }
217            //we found application-client.xml, if it won't parse it's an error.
218            try {
219                // parse it
220                XmlObject xmlObject = XmlBeansUtil.parse(specDD);
221                ApplicationClientDocument appClientDoc = convertToApplicationClientSchema(xmlObject);
222                appClient = appClientDoc.getApplicationClient();
223            } catch (XmlException e) {
224                throw new DeploymentException("Unable to parse application-client.xml", e);
225            }
226    
227            // parse vendor dd
228            boolean standAlone = earEnvironment == null;
229            GerApplicationClientType gerAppClient = getGeronimoAppClient(plan, moduleFile, standAlone, targetPath, appClient, earEnvironment);
230    
231    
232            EnvironmentType clientEnvironmentType = gerAppClient.getClientEnvironment();
233            Environment clientEnvironment = EnvironmentBuilder.buildEnvironment(clientEnvironmentType, defaultClientEnvironment);
234            if (standAlone) {
235                String name = new File(moduleFile.getName()).getName();
236                idBuilder.resolve(clientEnvironment, name + "_" + name, "jar");
237            } else {
238                Artifact earConfigId = earEnvironment.getConfigId();
239                idBuilder.resolve(clientEnvironment, earConfigId.getArtifactId() + "_" + targetPath, "jar");
240            }
241            EnvironmentType serverEnvironmentType = gerAppClient.getServerEnvironment();
242            Environment serverEnvironment = EnvironmentBuilder.buildEnvironment(serverEnvironmentType, defaultServerEnvironment);
243            if (!standAlone) {
244                EnvironmentBuilder.mergeEnvironments(earEnvironment, serverEnvironment);
245                serverEnvironment = earEnvironment;
246                if (!serverEnvironment.getConfigId().isResolved()) {
247                    throw new IllegalStateException("Server environment module ID should be fully resolved (not " + serverEnvironment.getConfigId() + ")");
248                }
249            } else {
250                idBuilder.resolve(serverEnvironment, new File(moduleFile.getName()).getName(), "jar");
251            }
252    
253            namingBuilders.buildEnvironment(appClient, gerAppClient, clientEnvironment);
254    
255            if (earName == null) {
256                earName = naming.createRootName(earEnvironment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
257            }
258    
259            //always use the artifactId of the app client as the name component of the module name (on the server).
260            AbstractName  moduleName = naming.createChildName(earName, clientEnvironment.getConfigId().toString(), NameFactory.APP_CLIENT_MODULE);
261            AbstractName clientBaseName = naming.createRootName(clientEnvironment.getConfigId(), clientEnvironment.getConfigId().toString(), NameFactory.J2EE_APPLICATION);
262    
263            //start installing the resource adapters in the client.
264            Collection resourceModules = new ArrayList();
265            GerResourceType[] resources = gerAppClient.getResourceArray();
266            for (int i = 0; i < resources.length; i++) {
267                GerResourceType resource = resources[i];
268                String path;
269                JarFile connectorFile;
270                if (resource.isSetExternalRar()) {
271                    path = resource.getExternalRar().trim();
272                    Artifact artifact = Artifact.create(path);
273                    File file = null;
274                    for (Iterator iterator = repositories.iterator(); iterator.hasNext();) {
275                        Repository repository = (Repository) iterator.next();
276                        if (repository.contains(artifact)) {
277                            file = repository.getLocation(artifact);
278                            break;
279                        }
280                    }
281                    if (file == null) {
282                        throw new DeploymentException("Missing rar in repositories: " + path);
283                    }
284                    try {
285                        connectorFile = new JarFile(file);
286                    } catch (IOException e) {
287                        throw new DeploymentException("Could not access rar contents", e);
288                    }
289                } else {
290                    path = resource.getInternalRar();
291                    try {
292                        connectorFile = new NestedJarFile(moduleFile, path);
293                    } catch (IOException e) {
294                        throw new DeploymentException("Could not locate connector inside ear", e);
295                    }
296                }
297                XmlObject connectorPlan = resource.getConnector();
298                Module connectorModule = getConnectorModuleBuilder().createModule(connectorPlan, connectorFile, path, null, clientEnvironment, null, clientBaseName, naming, idBuilder);
299                resourceModules.add(connectorModule);
300            }
301    
302            return new AppClientModule(standAlone, moduleName, clientBaseName, serverEnvironment, clientEnvironment, moduleFile, targetPath, appClient, gerAppClient, specDD, resourceModules);
303        }
304    
305        GerApplicationClientType getGeronimoAppClient(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, ApplicationClientType appClient, Environment environment) throws DeploymentException {
306            GerApplicationClientType gerAppClient;
307            XmlObject rawPlan = null;
308            try {
309                // load the geronimo-application-client.xml from either the supplied plan or from the earFile
310                try {
311                    if (plan instanceof XmlObject) {
312                        rawPlan = (XmlObject) plan;
313                    } else {
314                        if (plan != null) {
315                            rawPlan = XmlBeansUtil.parse((File) plan);
316                        } else {
317                            URL path = DeploymentUtil.createJarURL(moduleFile, "META-INF/geronimo-application-client.xml");
318                            rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
319                        }
320                    }
321                } catch (IOException e) {
322                    //exception means we create default
323                }
324    
325                // if we got one extract the validate it otherwise create a default one
326                if (rawPlan != null) {
327                    gerAppClient = (GerApplicationClientType) SchemaConversionUtils.fixGeronimoSchema(rawPlan, GerApplicationClientDocument.type.getDocumentElementName(), GerApplicationClientType.type);
328                } else {
329                    String path;
330                    if (standAlone) {
331                        // default configId is based on the moduleFile name
332                        path = new File(moduleFile.getName()).getName();
333                    } else {
334                        // default configId is based on the module uri from the application.xml
335                        path = targetPath;
336                    }
337                    gerAppClient = createDefaultPlan(path, appClient, standAlone, environment);
338                }
339            } catch (XmlException e) {
340                throw new DeploymentException(e);
341            }
342            return gerAppClient;
343        }
344    
345        private GerApplicationClientType createDefaultPlan(String name, ApplicationClientType appClient, boolean standAlone, Environment environment) {
346            String id = appClient.getId();
347            if (id == null) {
348                id = name;
349                if (id.endsWith(".jar")) {
350                    id = id.substring(0, id.length() - 4);
351                }
352                if (id.endsWith("/")) {
353                    id = id.substring(0, id.length() - 1);
354                }
355            }
356    
357            GerApplicationClientType geronimoAppClient = GerApplicationClientType.Factory.newInstance();
358            EnvironmentType clientEnvironmentType = geronimoAppClient.addNewClientEnvironment();
359            EnvironmentType serverEnvironmentType = geronimoAppClient.addNewServerEnvironment();
360            //TODO configid fill in environment with configids
361            // set the parentId and configId
362    //        if (standAlone) {
363    //            geronimoAppClient.setClientConfigId(id);
364    //            geronimoAppClient.setConfigId(id + "/server");
365    //        } else {
366    //            geronimoAppClient.setClientConfigId(earConfigId.getPath() + "/" + id);
367    //             not used but we need to have a value
368    //            geronimoAppClient.setConfigId(id);
369    //        }
370            return geronimoAppClient;
371        }
372    
373        static ApplicationClientDocument convertToApplicationClientSchema(XmlObject xmlObject) throws XmlException {
374            if (ApplicationClientDocument.type.equals(xmlObject.schemaType())) {
375                XmlBeansUtil.validateDD(xmlObject);
376                return (ApplicationClientDocument) xmlObject;
377            }
378            XmlCursor cursor = xmlObject.newCursor();
379            XmlCursor moveable = xmlObject.newCursor();
380            String schemaLocationURL = "http://java.sun.com/xml/ns/j2ee/application-client_1_4.xsd";
381            String version = "1.4";
382            try {
383                SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.J2EE_NAMESPACE, schemaLocationURL, version);
384                cursor.toStartDoc();
385                cursor.toChild(SchemaConversionUtils.J2EE_NAMESPACE, "application-client");
386                cursor.toFirstChild();
387                SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.J2EE_NAMESPACE, cursor, moveable);
388            } finally {
389                cursor.dispose();
390                moveable.dispose();
391            }
392            XmlObject result = xmlObject.changeType(ApplicationClientDocument.type);
393            if (result != null) {
394                XmlBeansUtil.validateDD(result);
395                return (ApplicationClientDocument) result;
396            }
397            XmlBeansUtil.validateDD(xmlObject);
398            return (ApplicationClientDocument) xmlObject;
399    
400        }
401    
402        public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repositories) throws DeploymentException {
403            // extract the app client jar file into a standalone packed jar file and add the contents to the output
404            JarFile moduleFile = module.getModuleFile();
405            try {
406                earContext.addIncludeAsPackedJar(URI.create(module.getTargetPath()), moduleFile);
407            } catch (IOException e) {
408                throw new DeploymentException("Unable to copy app client module jar into configuration: " + moduleFile.getName());
409            }
410            AppClientModule appClientModule = (AppClientModule) module;
411            appClientModule.setEarFile(earFile);
412            //create the ear context for the app client.
413            Environment clientEnvironment = appClientModule.getClientEnvironment();
414    //        if (!appClientModule.isStandAlone() || clientEnvironment.getConfigId() == null) {
415    //            Artifact earConfigId = earContext.getConfigID();
416    //            Artifact configId = new Artifact(earConfigId.getGroupId(), earConfigId.getArtifactId() + "_" + module.getTargetPath(), earConfigId.getVersion(), "car");
417    //            clientEnvironment.setConfigId(configId);
418    //        }
419    
420            File appClientDir;
421            try {
422                appClientDir = targetConfigurationStore.createNewConfigurationDir(clientEnvironment.getConfigId());
423            } catch (ConfigurationAlreadyExistsException e) {
424                throw new DeploymentException(e);
425            }
426    
427            // construct the app client deployment context... this is the same class used by the ear context
428            EARContext appClientDeploymentContext;
429            try {
430    
431                appClientDeploymentContext = new EARContext(appClientDir,
432                        null,
433                        clientEnvironment,
434                        ConfigurationModuleType.CAR,
435                        earContext.getNaming(),
436                        earContext.getConfigurationManager(),
437                        null, //no server name needed on client
438                        appClientModule.getAppClientName(),
439                        transactionManagerObjectName,
440                        connectionTrackerObjectName,
441                        null,
442                        null,
443                        corbaGBeanObjectName
444                );
445                appClientModule.setEarContext(appClientDeploymentContext);
446                appClientModule.setRootEarContext(earContext);
447            } catch (DeploymentException e) {
448                cleanupAppClientDir(appClientDir);
449                throw e;
450            }
451            for (Iterator resources = appClientModule.getResourceModules().iterator(); resources.hasNext();) {
452                ConnectorModule connectorModule = (ConnectorModule) resources.next();
453                getConnectorModuleBuilder().installModule(connectorModule.getModuleFile(), appClientDeploymentContext, connectorModule, configurationStores, targetConfigurationStore, repositories);
454            }
455    
456        }
457    
458        public void initContext(EARContext earContext, Module clientModule, ClassLoader cl) throws DeploymentException {
459            AppClientModule appClientModule = ((AppClientModule) clientModule);
460            for (Iterator resources = appClientModule.getResourceModules().iterator(); resources.hasNext();) {
461                ConnectorModule connectorModule = (ConnectorModule) resources.next();
462                getConnectorModuleBuilder().initContext(appClientModule.getEarContext(), connectorModule, cl);
463            }
464        }
465    
466        public void addGBeans(EARContext earContext, Module module, ClassLoader earClassLoader, Collection repositories) throws DeploymentException {
467    
468            AppClientModule appClientModule = (AppClientModule) module;
469    
470            ApplicationClientType appClient = (ApplicationClientType) appClientModule.getSpecDD();
471            GerApplicationClientType geronimoAppClient = (GerApplicationClientType) appClientModule.getVendorDD();
472    
473            // get the app client main class
474            JarFile moduleFile = module.getModuleFile();
475            String mainClasss;
476            try {
477                Manifest manifest = moduleFile.getManifest();
478                if (manifest == null) {
479                    throw new DeploymentException("App client module jar does not contain a manifest: " + moduleFile.getName());
480                }
481                mainClasss = manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
482                if (mainClasss == null) {
483                    throw new DeploymentException("App client module jar does not have Main-Class defined in the manifest: " + moduleFile.getName());
484                }
485                String classPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
486                if (module.isStandAlone() && classPath != null) {
487                    throw new DeploymentException("Manifest class path entry is not allowed in a standalone jar (J2EE 1.4 Section 8.2)");
488                }
489            } catch (IOException e) {
490                throw new DeploymentException("Could not get manifest from app client module: " + moduleFile.getName());
491            }
492    
493            // generate the object name for the app client
494            AbstractName appClientModuleName = appClientModule.getModuleName();
495    
496            // create a gbean for the app client module and add it to the ear
497            GBeanData appClientModuleGBeanData = new GBeanData(appClientModuleName, J2EEAppClientModuleImpl.GBEAN_INFO);
498            try {
499                appClientModuleGBeanData.setReferencePattern("J2EEServer", earContext.getServerName());
500                if (!module.isStandAlone()) {
501                    appClientModuleGBeanData.setReferencePattern("J2EEApplication", earContext.getModuleName());
502                }
503                appClientModuleGBeanData.setAttribute("deploymentDescriptor", appClientModule.getOriginalSpecDD());
504    
505            } catch (Exception e) {
506                throw new DeploymentException("Unable to initialize AppClientModule GBean", e);
507            }
508            try {
509                earContext.addGBean(appClientModuleGBeanData);
510            } catch (GBeanAlreadyExistsException e) {
511                throw new DeploymentException("Could not add application client module gbean to configuration", e);
512            }
513    
514            EARContext appClientDeploymentContext = appClientModule.getEarContext();
515    
516            // Create a Module ID Builder defaulting to similar settings to use for any children we create
517            ModuleIDBuilder idBuilder = new ModuleIDBuilder();
518            idBuilder.setDefaultGroup(appClientModule.getClientEnvironment().getConfigId().getGroupId());
519            idBuilder.setDefaultVersion(appClientModule.getClientEnvironment().getConfigId().getVersion());
520            try {
521                try {
522    
523                    //register the message destinations in the app client ear context.
524                    namingBuilders.initContext(appClient, geronimoAppClient, appClientDeploymentContext.getConfiguration(), earContext.getConfiguration(), appClientModule);
525                    // extract the client Jar file into a standalone packed jar file and add the contents to the output
526                    URI moduleBase = new URI(appClientModule.getTargetPath());
527                    try {
528                        appClientDeploymentContext.addIncludeAsPackedJar(moduleBase, moduleFile);
529                    } catch (IOException e) {
530                        throw new DeploymentException("Unable to copy app client module jar into configuration: " + moduleFile.getName());
531                    }
532    
533                    // add manifest class path entries to the app client context
534                    addManifestClassPath(appClientDeploymentContext, appClientModule.getEarFile(), moduleFile, moduleBase);
535    
536                    // get the classloader
537                    ClassLoader appClientClassLoader = appClientDeploymentContext.getClassLoader();
538    
539                    // pop in all the gbeans declared in the geronimo app client file
540                    if (geronimoAppClient != null) {
541                        serviceBuilder.build(geronimoAppClient, appClientDeploymentContext, appClientDeploymentContext);
542                        //deploy the resource adapters specified in the geronimo-application.xml
543    
544                        for (Iterator resources = appClientModule.getResourceModules().iterator(); resources.hasNext();) {
545                            ConnectorModule connectorModule = (ConnectorModule) resources.next();
546                            getConnectorModuleBuilder().addGBeans(appClientDeploymentContext, connectorModule, appClientClassLoader, repositories);
547                        }
548                    }
549    
550                    // add the app client static jndi provider
551                    //TODO track resource ref shared and app managed security
552                    AbstractName jndiContextName = earContext.getNaming().createChildName(appClientDeploymentContext.getModuleName(), "StaticJndiContext", "StaticJndiContext");
553                    GBeanData jndiContextGBeanData = new GBeanData(jndiContextName, StaticJndiContextPlugin.GBEAN_INFO);
554                    try {
555                        Map buildingContext = new HashMap();
556                        buildingContext.put(NamingBuilder.JNDI_KEY, new HashMap());
557                        buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, jndiContextName);
558                        Configuration localConfiguration = appClientDeploymentContext.getConfiguration();
559                        Configuration remoteConfiguration = earContext.getConfiguration();
560                        namingBuilders.buildNaming(appClient, geronimoAppClient, localConfiguration, remoteConfiguration, appClientModule, buildingContext);
561                        jndiContextGBeanData.setAttribute("context", buildingContext.get(NamingBuilder.JNDI_KEY));
562                    } catch (DeploymentException e) {
563                        throw e;
564                    } catch (Exception e) {
565                        throw new DeploymentException("Unable to construct jndi context for AppClientModule GBean", e);
566                    }
567                    appClientDeploymentContext.addGBean(jndiContextGBeanData);
568    
569                    // finally add the app client container
570                    AbstractName appClientContainerName = appClientDeploymentContext.getModuleName();
571                    GBeanData appClientContainerGBeanData = new GBeanData(appClientContainerName, AppClientContainer.GBEAN_INFO);
572                    try {
573                        appClientContainerGBeanData.setAttribute("mainClassName", mainClasss);
574                        appClientContainerGBeanData.setAttribute("appClientModuleName", appClientModuleName);
575                        String callbackHandlerClassName = null;
576                        if (appClient.isSetCallbackHandler()) {
577                            callbackHandlerClassName = appClient.getCallbackHandler().getStringValue().trim();
578                        }
579                        if (geronimoAppClient.isSetCallbackHandler()) {
580                            callbackHandlerClassName = geronimoAppClient.getCallbackHandler().trim();
581                        }
582                        String realmName = null;
583                        if (geronimoAppClient.isSetRealmName()) {
584                            realmName = geronimoAppClient.getRealmName().trim();
585                        }
586                        if (callbackHandlerClassName != null && realmName == null) {
587                            throw new DeploymentException("You must specify a realm name with the callback handler");
588                        }
589                        if (realmName != null) {
590                            appClientContainerGBeanData.setAttribute("realmName", realmName);
591                            appClientContainerGBeanData.setAttribute("callbackHandlerClassName", callbackHandlerClassName);
592                        } else if (geronimoAppClient.isSetDefaultPrincipal()) {
593                            DefaultPrincipal defaultPrincipal = getSecurityBuilder().buildDefaultPrincipal(geronimoAppClient.getDefaultPrincipal());
594                            appClientContainerGBeanData.setAttribute("defaultPrincipal", defaultPrincipal);
595                        }
596                        appClientContainerGBeanData.setReferencePattern("JNDIContext", jndiContextName);
597                    } catch (Exception e) {
598                        throw new DeploymentException("Unable to initialize AppClientModule GBean", e);
599                    }
600                    appClientDeploymentContext.addGBean(appClientContainerGBeanData);
601    
602                    // get the configuration data
603                    earContext.addAdditionalDeployment(appClientDeploymentContext.getConfigurationData());
604                } finally {
605                    if (appClientDeploymentContext != null) {
606                        try {
607                            appClientDeploymentContext.close();
608                        } catch (IOException e) {
609                            //nothing we can do
610                        }
611                    }
612                }
613    
614            } catch (Throwable e) {
615                File appClientDir = appClientDeploymentContext.getBaseDir();
616                cleanupAppClientDir(appClientDir);
617                if (e instanceof Error) {
618                    throw (Error) e;
619                } else if (e instanceof DeploymentException) {
620                    throw (DeploymentException) e;
621                } else if (e instanceof Exception) {
622                    throw new DeploymentException(e);
623                }
624                throw new Error(e);
625            }
626        }
627    
628        public String getSchemaNamespace() {
629            return GERAPPCLIENT_NAMESPACE;
630        }
631    
632        public void addManifestClassPath(DeploymentContext deploymentContext, JarFile earFile, JarFile jarFile, URI jarFileLocation) throws DeploymentException {
633            Manifest manifest;
634            try {
635                manifest = jarFile.getManifest();
636            } catch (IOException e) {
637                throw new DeploymentException("Could not read manifest: " + jarFileLocation);
638            }
639    
640            if (manifest == null) {
641                return;
642            }
643            String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
644            if (manifestClassPath == null) {
645                return;
646            }
647    
648            for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
649                String path = tokenizer.nextToken();
650    
651                URI pathUri;
652                try {
653                    pathUri = new URI(path);
654                } catch (URISyntaxException e) {
655                    throw new DeploymentException("Invalid manifest classpath entry: jarFile=" + jarFileLocation + ", path=" + path);
656                }
657    
658                if (!pathUri.getPath().endsWith(".jar")) {
659                    throw new DeploymentException("Manifest class path entries must end with the .jar extension (J2EE 1.4 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path);
660                }
661                if (pathUri.isAbsolute()) {
662                    throw new DeploymentException("Manifest class path entries must be relative (J2EE 1.4 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path);
663                }
664    
665                // determine the target file
666                URI classPathJarLocation = jarFileLocation.resolve(pathUri);
667                File classPathFile = deploymentContext.getTargetFile(classPathJarLocation);
668    
669                // we only recuse if the path entry is not already in the output context
670                // this will work for all current cases, but may not work in the future
671                if (!classPathFile.exists()) {
672                    // check if the path exists in the earFile
673                    ZipEntry entry = earFile.getEntry(classPathJarLocation.getPath());
674                    if (entry == null) {
675                        throw new DeploymentException("Cound not find manifest class path entry: jarFile=" + jarFileLocation + ", path=" + path);
676                    }
677    
678                    try {
679                        // copy the file into the output context
680                        deploymentContext.addFile(classPathJarLocation, earFile, entry);
681                    } catch (IOException e) {
682                        throw new DeploymentException("Cound not copy manifest class path entry into configuration: jarFile=" + jarFileLocation + ", path=" + path, e);
683                    }
684    
685                    JarFile classPathJarFile;
686                    try {
687                        classPathJarFile = new JarFile(classPathFile);
688                    } catch (IOException e) {
689                        throw new DeploymentException("Manifest class path entries must be a valid jar file (J2EE 1.4 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path, e);
690                    }
691    
692                    // add the client jars of this class path jar
693                    addManifestClassPath(deploymentContext, earFile, classPathJarFile, classPathJarLocation);
694                }
695            }
696        }
697    
698        private boolean cleanupAppClientDir(File configurationDir) {
699            LinkedList cannotBeDeletedList = new LinkedList();
700    
701            if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) {
702                // Output a message to help user track down file problem
703                log.warn("Unable to delete " + cannotBeDeletedList.size() +
704                        " files while recursively deleting directory "
705                        + configurationDir + LINE_SEP +
706                        "The first file that could not be deleted was:" + LINE_SEP + "  " +
707                        (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : ""));
708                return false;
709            }
710            return true;
711        }
712    
713        public static final GBeanInfo GBEAN_INFO;
714    
715        static {
716            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(AppClientModuleBuilder.class, NameFactory.MODULE_BUILDER);
717            infoBuilder.addAttribute("defaultClientEnvironment", Environment.class, true, true);
718            infoBuilder.addAttribute("defaultServerEnvironment", Environment.class, true, true);
719            infoBuilder.addAttribute("transactionManagerObjectName", AbstractNameQuery.class, true);
720            infoBuilder.addAttribute("connectionTrackerObjectName", AbstractNameQuery.class, true);
721            infoBuilder.addAttribute("corbaGBeanObjectName", AbstractNameQuery.class, true);
722            infoBuilder.addReference("Repositories", Repository.class, "Repository");
723            infoBuilder.addReference("ConnectorModuleBuilder", ModuleBuilder.class, NameFactory.MODULE_BUILDER);
724            infoBuilder.addReference("SecurityBuilder", SecurityBuilder.class, NameFactory.MODULE_BUILDER);
725            infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
726            infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
727    
728            infoBuilder.addInterface(ModuleBuilder.class);
729    
730            infoBuilder.setConstructor(new String[]{"transactionManagerObjectName",
731                    "connectionTrackerObjectName",
732                    "corbaGBeanObjectName",
733                    "Repositories",
734                    "ConnectorModuleBuilder",
735                    "SecurityBuilder",
736                    "ServiceBuilders",
737                    "NamingBuilders",
738                    "defaultClientEnvironment",
739                    "defaultServerEnvironment",
740            });
741    
742            GBEAN_INFO = infoBuilder.getBeanInfo();
743        }
744    
745        public static GBeanInfo getGBeanInfo() {
746            return GBEAN_INFO;
747        }
748    
749    }