View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.client.builder;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.net.URI;
23  import java.net.URISyntaxException;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.Iterator;
29  import java.util.LinkedList;
30  import java.util.Map;
31  import java.util.StringTokenizer;
32  import java.util.HashMap;
33  import java.util.jar.Attributes;
34  import java.util.jar.JarFile;
35  import java.util.jar.Manifest;
36  import java.util.zip.ZipEntry;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.geronimo.client.AppClientContainer;
41  import org.apache.geronimo.client.StaticJndiContextPlugin;
42  import org.apache.geronimo.common.DeploymentException;
43  import org.apache.geronimo.deployment.DeploymentContext;
44  import org.apache.geronimo.deployment.ModuleIDBuilder;
45  import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
46  import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
47  import org.apache.geronimo.deployment.service.EnvironmentBuilder;
48  import org.apache.geronimo.deployment.util.DeploymentUtil;
49  import org.apache.geronimo.deployment.util.NestedJarFile;
50  import org.apache.geronimo.deployment.xbeans.EnvironmentType;
51  import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
52  import org.apache.geronimo.gbean.AbstractName;
53  import org.apache.geronimo.gbean.AbstractNameQuery;
54  import org.apache.geronimo.gbean.GBeanData;
55  import org.apache.geronimo.gbean.GBeanInfo;
56  import org.apache.geronimo.gbean.GBeanInfoBuilder;
57  import org.apache.geronimo.gbean.SingleElementCollection;
58  import org.apache.geronimo.j2ee.deployment.AppClientModule;
59  import org.apache.geronimo.j2ee.deployment.EARContext;
60  import org.apache.geronimo.j2ee.deployment.Module;
61  import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
62  import org.apache.geronimo.j2ee.deployment.SecurityBuilder;
63  import org.apache.geronimo.j2ee.deployment.CorbaGBeanNameSource;
64  import org.apache.geronimo.j2ee.deployment.NamingBuilder;
65  import org.apache.geronimo.j2ee.deployment.NamingBuilderCollection;
66  import org.apache.geronimo.j2ee.deployment.ConnectorModule;
67  import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
68  import org.apache.geronimo.j2ee.management.impl.J2EEAppClientModuleImpl;
69  import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
70  import org.apache.geronimo.kernel.Naming;
71  import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
72  import org.apache.geronimo.kernel.config.ConfigurationModuleType;
73  import org.apache.geronimo.kernel.config.ConfigurationStore;
74  import org.apache.geronimo.kernel.config.Configuration;
75  import org.apache.geronimo.kernel.repository.Artifact;
76  import org.apache.geronimo.kernel.repository.Environment;
77  import org.apache.geronimo.kernel.repository.Repository;
78  import org.apache.geronimo.schema.SchemaConversionUtils;
79  import org.apache.geronimo.security.deploy.DefaultPrincipal;
80  import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientDocument;
81  import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientType;
82  import org.apache.geronimo.xbeans.geronimo.client.GerResourceType;
83  import org.apache.geronimo.xbeans.geronimo.naming.GerAbstractNamingEntryDocument;
84  import org.apache.geronimo.xbeans.j2ee.ApplicationClientDocument;
85  import org.apache.geronimo.xbeans.j2ee.ApplicationClientType;
86  import org.apache.xmlbeans.XmlException;
87  import org.apache.xmlbeans.XmlObject;
88  import org.apache.xmlbeans.XmlCursor;
89  
90  
91  /**
92   * @version $Rev:385232 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
93   */
94  public class AppClientModuleBuilder implements ModuleBuilder, CorbaGBeanNameSource {
95      private static final Log log = LogFactory.getLog(AppClientModuleBuilder.class);
96      private static final String LINE_SEP = System.getProperty("line.separator");
97  
98      private final Environment defaultClientEnvironment;
99      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 }