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 }