001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.client.builder;
018
019 import java.io.File;
020 import java.io.IOException;
021 import java.net.URI;
022 import java.net.URISyntaxException;
023 import java.net.URL;
024 import java.util.ArrayList;
025 import java.util.Collection;
026 import java.util.Collections;
027 import java.util.HashMap;
028 import java.util.LinkedList;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.StringTokenizer;
032 import java.util.LinkedHashSet;
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.ClassPathList;
044 import org.apache.geronimo.deployment.DeploymentContext;
045 import org.apache.geronimo.deployment.ModuleIDBuilder;
046 import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
047 import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
048 import org.apache.geronimo.deployment.ModuleList;
049 import org.apache.geronimo.deployment.service.EnvironmentBuilder;
050 import org.apache.geronimo.deployment.service.GBeanBuilder;
051 import org.apache.geronimo.deployment.util.DeploymentUtil;
052 import org.apache.geronimo.deployment.util.NestedJarFile;
053 import org.apache.geronimo.deployment.xbeans.EnvironmentType;
054 import org.apache.geronimo.deployment.xbeans.PatternType;
055 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
056 import org.apache.geronimo.gbean.AbstractName;
057 import org.apache.geronimo.gbean.AbstractNameQuery;
058 import org.apache.geronimo.gbean.GBeanData;
059 import org.apache.geronimo.gbean.GBeanInfo;
060 import org.apache.geronimo.gbean.GBeanInfoBuilder;
061 import org.apache.geronimo.gbean.SingleElementCollection;
062 import org.apache.geronimo.j2ee.deployment.AppClientModule;
063 import org.apache.geronimo.j2ee.deployment.ConnectorModule;
064 import org.apache.geronimo.j2ee.deployment.CorbaGBeanNameSource;
065 import org.apache.geronimo.j2ee.deployment.EARContext;
066 import org.apache.geronimo.j2ee.deployment.Module;
067 import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
068 import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
069 import org.apache.geronimo.j2ee.deployment.NamingBuilder;
070 import org.apache.geronimo.j2ee.deployment.NamingBuilderCollection;
071 import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedApplicationClient;
072 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
073 import org.apache.geronimo.j2ee.management.impl.J2EEAppClientModuleImpl;
074 import org.apache.geronimo.j2ee.ApplicationInfo;
075 import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
076 import org.apache.geronimo.kernel.Naming;
077 import org.apache.geronimo.kernel.config.Configuration;
078 import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
079 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
080 import org.apache.geronimo.kernel.config.ConfigurationStore;
081 import org.apache.geronimo.kernel.repository.Artifact;
082 import org.apache.geronimo.kernel.repository.Environment;
083 import org.apache.geronimo.kernel.repository.Repository;
084 import org.apache.geronimo.kernel.repository.ArtifactResolver;
085 import org.apache.geronimo.kernel.repository.MissingDependencyException;
086 import org.apache.geronimo.schema.SchemaConversionUtils;
087 import org.apache.geronimo.security.deploy.SubjectInfo;
088 import org.apache.geronimo.security.deployment.SecurityConfiguration;
089 import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientDocument;
090 import org.apache.geronimo.xbeans.geronimo.client.GerApplicationClientType;
091 import org.apache.geronimo.xbeans.geronimo.client.GerResourceType;
092 import org.apache.geronimo.xbeans.geronimo.naming.GerAbstractNamingEntryDocument;
093 import org.apache.geronimo.xbeans.geronimo.security.GerSubjectInfoType;
094 import org.apache.geronimo.xbeans.javaee.ApplicationClientDocument;
095 import org.apache.geronimo.xbeans.javaee.ApplicationClientType;
096 import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
097 import org.apache.xbean.finder.ClassFinder;
098 import org.apache.xmlbeans.XmlCursor;
099 import org.apache.xmlbeans.XmlException;
100 import org.apache.xmlbeans.XmlObject;
101
102
103 /**
104 * @version $Rev:385232 $ $Date: 2007-07-31 05:39:53 -0400 (Tue, 31 Jul 2007) $
105 */
106 public class AppClientModuleBuilder implements ModuleBuilder, CorbaGBeanNameSource {
107 private static final Log log = LogFactory.getLog(AppClientModuleBuilder.class);
108 private static final String LINE_SEP = System.getProperty("line.separator");
109 private static final String GERAPPCLIENT_NAMESPACE = GerApplicationClientDocument.type.getDocumentElementName().getNamespaceURI();
110
111 private final Environment defaultClientEnvironment;
112 private final Environment defaultServerEnvironment;
113 private final AbstractNameQuery corbaGBeanObjectName;
114
115 private final AbstractNameQuery transactionManagerObjectName;
116 private final AbstractNameQuery connectionTrackerObjectName;
117 private final AbstractNameQuery credentialStoreName;
118 private final SingleElementCollection connectorModuleBuilder;
119 private final NamespaceDrivenBuilderCollection serviceBuilder;
120 private final NamingBuilderCollection namingBuilders;
121 private final Collection<ModuleBuilderExtension> moduleBuilderExtensions;
122
123 private final Collection<Repository> repositories;
124
125 private final ArtifactResolver clientArtifactResolver;
126
127 public AppClientModuleBuilder(Environment defaultClientEnvironment,
128 Environment defaultServerEnvironment,
129 AbstractNameQuery transactionManagerObjectName,
130 AbstractNameQuery connectionTrackerObjectName,
131 AbstractNameQuery corbaGBeanObjectName,
132 AbstractNameQuery credentialStoreName,
133 Collection<Repository> repositories,
134 ModuleBuilder connectorModuleBuilder,
135 NamespaceDrivenBuilder serviceBuilder,
136 Collection<NamingBuilder> namingBuilders,
137 Collection<ModuleBuilderExtension> moduleBuilderExtensions,
138 ArtifactResolver clientArtifactResolver) {
139 this(defaultClientEnvironment,
140 defaultServerEnvironment,
141 transactionManagerObjectName,
142 connectionTrackerObjectName,
143 corbaGBeanObjectName,
144 credentialStoreName, repositories, new SingleElementCollection<ModuleBuilder>(connectorModuleBuilder),
145 serviceBuilder == null ? Collections.EMPTY_SET : Collections.singleton(serviceBuilder),
146 namingBuilders == null ? Collections.EMPTY_SET : namingBuilders,
147 moduleBuilderExtensions,
148 clientArtifactResolver);
149 }
150
151 public AppClientModuleBuilder(AbstractNameQuery transactionManagerObjectName,
152 AbstractNameQuery connectionTrackerObjectName,
153 AbstractNameQuery corbaGBeanObjectName,
154 AbstractNameQuery credentialStoreName,
155 Collection<Repository> repositories,
156 Collection<ModuleBuilder> connectorModuleBuilder,
157 Collection<NamespaceDrivenBuilder> serviceBuilder,
158 Collection<NamingBuilder> namingBuilders,
159 Collection<ModuleBuilderExtension> moduleBuilderExtensions,
160 ArtifactResolver clientArtifactResolver,
161 Environment defaultClientEnvironment,
162 Environment defaultServerEnvironment
163 ) {
164 this(defaultClientEnvironment,
165 defaultServerEnvironment,
166 transactionManagerObjectName,
167 connectionTrackerObjectName,
168 corbaGBeanObjectName,
169 credentialStoreName, repositories,
170 new SingleElementCollection<ModuleBuilder>(connectorModuleBuilder),
171 serviceBuilder,
172 namingBuilders,
173 moduleBuilderExtensions,
174 clientArtifactResolver);
175 }
176
177 private AppClientModuleBuilder(Environment defaultClientEnvironment,
178 Environment defaultServerEnvironment,
179 AbstractNameQuery transactionManagerObjectName,
180 AbstractNameQuery connectionTrackerObjectName,
181 AbstractNameQuery corbaGBeanObjectName,
182 AbstractNameQuery credentialStoreName,
183 Collection<Repository> repositories,
184 SingleElementCollection<ModuleBuilder> connectorModuleBuilder,
185 Collection<NamespaceDrivenBuilder> serviceBuilder,
186 Collection<NamingBuilder> namingBuilders,
187 Collection<ModuleBuilderExtension> moduleBuilderExtensions,
188 ArtifactResolver clientArtifactResolver) {
189 this.defaultClientEnvironment = defaultClientEnvironment;
190 this.defaultServerEnvironment = defaultServerEnvironment;
191 this.corbaGBeanObjectName = corbaGBeanObjectName;
192 this.transactionManagerObjectName = transactionManagerObjectName;
193 this.connectionTrackerObjectName = connectionTrackerObjectName;
194 this.credentialStoreName = credentialStoreName;
195 this.repositories = repositories;
196 this.connectorModuleBuilder = connectorModuleBuilder;
197 this.serviceBuilder = new NamespaceDrivenBuilderCollection(serviceBuilder, GBeanBuilder.SERVICE_QNAME);
198 this.namingBuilders = new NamingBuilderCollection(namingBuilders, GerAbstractNamingEntryDocument.type.getDocumentElementName());
199 this.moduleBuilderExtensions = moduleBuilderExtensions;
200 this.clientArtifactResolver = clientArtifactResolver;
201 }
202
203
204 public AbstractNameQuery getCorbaGBeanName() {
205 return corbaGBeanObjectName;
206 }
207
208 private ModuleBuilder getConnectorModuleBuilder() {
209 return (ModuleBuilder) connectorModuleBuilder.getElement();
210 }
211
212 public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
213 return createModule(plan, moduleFile, "app-client", null, null, null, naming, idBuilder);
214 }
215
216 public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
217 return createModule(plan, moduleFile, targetPath, specDDUrl, environment, earName, naming, idBuilder);
218 }
219
220 private Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment earEnvironment, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
221 assert moduleFile != null : "moduleFile is null";
222 assert targetPath != null : "targetPath is null";
223 assert !targetPath.endsWith("/") : "targetPath must not end with a '/'";
224 assert (earName == null) == (earEnvironment == null) : "if earName is not null you must supply earEnvironment as well";
225
226 boolean standAlone = earEnvironment == null;
227
228 // get the app client main class
229 String mainClass;
230 try {
231 Manifest manifest = moduleFile.getManifest();
232 if (manifest == null) {
233 throw new DeploymentException("App client module jar does not contain a manifest: " + moduleFile.getName());
234 }
235 mainClass = manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
236 if (mainClass == null) {
237 //not an app client
238 return null;
239 }
240 String classPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
241 if (standAlone && classPath != null) {
242 throw new DeploymentException("Manifest class path entry is not allowed in a standalone jar (JAVAEE 5 Section 8.2)");
243 }
244 } catch (IOException e) {
245 throw new DeploymentException("Could not get manifest from app client module: " + moduleFile.getName(), e);
246 }
247
248 String specDD;
249 ApplicationClientType appClient = null;
250 try {
251 if (specDDUrl == null) {
252 specDDUrl = DeploymentUtil.createJarURL(moduleFile, "META-INF/application-client.xml");
253 }
254
255 // read in the entire specDD as a string, we need this for getDeploymentDescriptor
256 // on the J2ee management object
257 specDD = DeploymentUtil.readAll(specDDUrl);
258 } catch (Exception e) {
259 //construct a default spec dd
260 ApplicationClientDocument appClientDoc = ApplicationClientDocument.Factory.newInstance();
261 appClientDoc.addNewApplicationClient();
262 appClient = appClientDoc.getApplicationClient();
263 specDD = appClientDoc.xmlText();
264 }
265
266 if (appClient == null) {
267 //we found application-client.xml, if it won't parse it's an error.
268 try {
269 // parse it
270 XmlObject xmlObject = XmlBeansUtil.parse(specDD);
271 ApplicationClientDocument appClientDoc = convertToApplicationClientSchema(xmlObject);
272 appClient = appClientDoc.getApplicationClient();
273 } catch (XmlException e) {
274 throw new DeploymentException("Unable to parse application-client.xml", e);
275 }
276 }
277
278 // parse vendor dd
279 GerApplicationClientType gerAppClient = getGeronimoAppClient(plan, moduleFile, standAlone, targetPath, appClient, earEnvironment);
280
281
282 EnvironmentType clientEnvironmentType = gerAppClient.getClientEnvironment();
283 Environment clientEnvironment = EnvironmentBuilder.buildEnvironment(clientEnvironmentType, defaultClientEnvironment);
284 if (standAlone) {
285 String name = new File(moduleFile.getName()).getName();
286 idBuilder.resolve(clientEnvironment, name + "_" + name, "jar");
287 } else {
288 Artifact earConfigId = earEnvironment.getConfigId();
289 idBuilder.resolve(clientEnvironment, earConfigId.getArtifactId() + "_" + targetPath, "jar");
290 }
291 EnvironmentType serverEnvironmentType = gerAppClient.getServerEnvironment();
292 Environment serverEnvironment = EnvironmentBuilder.buildEnvironment(serverEnvironmentType, defaultServerEnvironment);
293 if (!standAlone) {
294 EnvironmentBuilder.mergeEnvironments(earEnvironment, serverEnvironment);
295 serverEnvironment = earEnvironment;
296 if (!serverEnvironment.getConfigId().isResolved()) {
297 throw new IllegalStateException("Server environment module ID should be fully resolved (not " + serverEnvironment.getConfigId() + ")");
298 }
299 } else {
300 idBuilder.resolve(serverEnvironment, new File(moduleFile.getName()).getName(), "jar");
301 }
302
303 if (earName == null) {
304 earName = naming.createRootName(serverEnvironment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
305 }
306
307 //always use the artifactId of the app client as the name component of the module name (on the server).
308 AbstractName moduleName = naming.createChildName(earName, clientEnvironment.getConfigId().toString(), NameFactory.APP_CLIENT_MODULE);
309 AbstractName clientBaseName = naming.createRootName(clientEnvironment.getConfigId(), clientEnvironment.getConfigId().toString(), NameFactory.J2EE_APPLICATION);
310
311 //start installing the resource adapters in the client.
312 Collection<ConnectorModule> resourceModules = new ArrayList<ConnectorModule>();
313 GerResourceType[] resources = gerAppClient.getResourceArray();
314 for (GerResourceType resource : resources) {
315 String path;
316 JarFile connectorFile;
317 if (resource.isSetExternalRar()) {
318 PatternType externalRar = resource.getExternalRar();
319 String groupId = trim(externalRar.getGroupId());
320 String artifactId = trim(externalRar.getArtifactId());
321 String version = trim(externalRar.getVersion());
322 String type = trim(externalRar.getType());
323 Artifact artifact = new Artifact(groupId, artifactId, version, type);
324 try {
325 artifact = clientArtifactResolver.resolveInClassLoader(artifact);
326 } catch (MissingDependencyException e) {
327 throw new DeploymentException("Could not resolve external rar location in repository: " + artifact, e);
328 }
329 File file = null;
330 for (Repository repository : repositories) {
331 if (repository.contains(artifact)) {
332 file = repository.getLocation(artifact);
333 break;
334 }
335 }
336 if (file == null) {
337 throw new DeploymentException("Missing external rar in repositories: " + artifact);
338 }
339 try {
340 connectorFile = new JarFile(file);
341 } catch (IOException e) {
342 throw new DeploymentException("Could not access external rar contents for artifact: " + artifact, e);
343 }
344 path = artifact.toString();
345 } else {
346 path = resource.getInternalRar();
347 try {
348 connectorFile = new NestedJarFile(moduleFile, path);
349 } catch (IOException e) {
350 throw new DeploymentException("Could not locate connector inside ear", e);
351 }
352 }
353 XmlObject connectorPlan = resource.getConnector();
354 ConnectorModule connectorModule = (ConnectorModule) getConnectorModuleBuilder().createModule(connectorPlan, connectorFile, path, null, clientEnvironment, null, clientBaseName, naming, idBuilder);
355 resourceModules.add(connectorModule);
356 }
357
358 // Create the AnnotatedApp interface for the AppClientModule
359 AnnotatedApplicationClient annotatedApplicationClient = new AnnotatedApplicationClient(appClient, mainClass);
360
361 AppClientModule module = new AppClientModule(standAlone, moduleName, clientBaseName, serverEnvironment, clientEnvironment, moduleFile, targetPath, appClient, mainClass, gerAppClient, specDD, resourceModules, annotatedApplicationClient);
362 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
363 mbe.createModule(module, plan, moduleFile, targetPath, specDDUrl, clientEnvironment, null, earName, naming, idBuilder);
364 }
365 if (standAlone) {
366 ApplicationInfo appInfo = new ApplicationInfo(ConfigurationModuleType.CAR,
367 serverEnvironment,
368 earName,
369 null,
370 null,
371 null,
372 new LinkedHashSet<Module>(Collections.singleton(module)),
373 new ModuleList(),
374 null);
375 return appInfo;
376 } else {
377 return module;
378 }
379 }
380
381 private String trim(String s) {
382 if (s == null) {
383 return null;
384 }
385 return s.trim();
386 }
387
388 GerApplicationClientType getGeronimoAppClient(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, ApplicationClientType appClient, Environment environment) throws DeploymentException {
389 GerApplicationClientType gerAppClient;
390 XmlObject rawPlan = null;
391 try {
392 // load the geronimo-application-client.xml from either the supplied plan or from the earFile
393 try {
394 if (plan instanceof XmlObject) {
395 rawPlan = (XmlObject) plan;
396 } else {
397 if (plan != null) {
398 rawPlan = XmlBeansUtil.parse((File) plan);
399 } else {
400 URL path = DeploymentUtil.createJarURL(moduleFile, "META-INF/geronimo-application-client.xml");
401 rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
402 }
403 }
404 } catch (IOException e) {
405 //exception means we create default
406 }
407
408 // if we got one extract the validate it otherwise create a default one
409 if (rawPlan != null) {
410 gerAppClient = (GerApplicationClientType) SchemaConversionUtils.fixGeronimoSchema(rawPlan, GerApplicationClientDocument.type.getDocumentElementName(), GerApplicationClientType.type);
411 } else {
412 String path;
413 if (standAlone) {
414 // default configId is based on the moduleFile name
415 path = new File(moduleFile.getName()).getName();
416 } else {
417 // default configId is based on the module uri from the application.xml
418 path = targetPath;
419 }
420 gerAppClient = createDefaultPlan(path, appClient, standAlone, environment);
421 }
422 } catch (XmlException e) {
423 throw new DeploymentException("Unable to parse application plan", e);
424 }
425 return gerAppClient;
426 }
427
428 private GerApplicationClientType createDefaultPlan(String name, ApplicationClientType appClient, boolean standAlone, Environment environment) {
429 String id = appClient == null ? null : appClient.getId();
430 if (id == null) {
431 id = name;
432 if (id.endsWith(".jar")) {
433 id = id.substring(0, id.length() - 4);
434 }
435 if (id.endsWith("/")) {
436 id = id.substring(0, id.length() - 1);
437 }
438 }
439
440 GerApplicationClientType geronimoAppClient = GerApplicationClientType.Factory.newInstance();
441 EnvironmentType clientEnvironmentType = geronimoAppClient.addNewClientEnvironment();
442 EnvironmentType serverEnvironmentType = geronimoAppClient.addNewServerEnvironment();
443 //TODO configid fill in environment with configids
444 // set the parentId and configId
445 // if (standAlone) {
446 // geronimoAppClient.setClientConfigId(id);
447 // geronimoAppClient.setConfigId(id + "/server");
448 // } else {
449 // geronimoAppClient.setClientConfigId(earConfigId.getPath() + "/" + id);
450 // not used but we need to have a value
451 // geronimoAppClient.setConfigId(id);
452 // }
453 return geronimoAppClient;
454 }
455
456 static ApplicationClientDocument convertToApplicationClientSchema(XmlObject xmlObject) throws XmlException {
457 if (ApplicationClientDocument.type.equals(xmlObject.schemaType())) {
458 ApplicationClientType appClient = ((ApplicationClientDocument) xmlObject).getApplicationClient();
459 if ("5.0".equals(appClient.getVersion())) {
460 appClient.setVersion("5");
461 }
462 XmlBeansUtil.validateDD(xmlObject);
463 return (ApplicationClientDocument) xmlObject;
464 }
465 XmlCursor cursor = xmlObject.newCursor();
466 XmlCursor moveable = xmlObject.newCursor();
467 String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/application-client_5.xsd";
468 String version = "5";
469 try {
470 cursor.toStartDoc();
471 cursor.toFirstChild();
472 if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())
473 || "http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) {
474 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
475 XmlObject result = xmlObject.changeType(ApplicationClientDocument.type);
476 XmlBeansUtil.validateDD(result);
477 return (ApplicationClientDocument) result;
478 }
479
480 // otherwise assume DTD
481 SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
482 cursor.toStartDoc();
483 cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "application-client");
484 cursor.toFirstChild();
485 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
486 } finally {
487 cursor.dispose();
488 moveable.dispose();
489 }
490 XmlObject result = xmlObject.changeType(ApplicationClientDocument.type);
491 if (result != null) {
492 XmlBeansUtil.validateDD(result);
493 return (ApplicationClientDocument) result;
494 }
495 XmlBeansUtil.validateDD(xmlObject);
496 return (ApplicationClientDocument) xmlObject;
497
498 }
499
500 public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repositories) throws DeploymentException {
501 // extract the app client jar file into a standalone packed jar file and add the contents to the output
502 //This duplicates the copy in the app client's own configuration, made below.
503 //this should really only be done if there's a manifest classpath reference to the app client jar by another
504 //javaee module.
505 JarFile moduleFile = module.getModuleFile();
506 try {
507 earContext.addIncludeAsPackedJar(URI.create(module.getTargetPath()), moduleFile);
508 } catch (IOException e) {
509 throw new DeploymentException("Unable to copy app client module jar into configuration: " + moduleFile.getName(), e);
510 }
511 AppClientModule appClientModule = (AppClientModule) module;
512 appClientModule.setEarFile(earFile);
513 //create the ear context for the app client.
514 Environment clientEnvironment = appClientModule.getEnvironment();
515 // if (!appClientModule.isStandAlone() || clientEnvironment.getConfigId() == null) {
516 // Artifact earConfigId = earContext.getConfigID();
517 // Artifact configId = new Artifact(earConfigId.getGroupId(), earConfigId.getArtifactId() + "_" + module.getTargetPath(), earConfigId.getVersion(), "car");
518 // clientEnvironment.setConfigId(configId);
519 // }
520
521 File appClientDir;
522 try {
523 appClientDir = targetConfigurationStore.createNewConfigurationDir(clientEnvironment.getConfigId());
524 } catch (ConfigurationAlreadyExistsException e) {
525 throw new DeploymentException("Unable to create configuration directory for " + clientEnvironment.getConfigId(), e);
526 }
527
528 // construct the app client deployment context... this is the same class used by the ear context
529 EARContext appClientDeploymentContext;
530 try {
531
532 appClientDeploymentContext = new EARContext(appClientDir,
533 null,
534 clientEnvironment,
535 ConfigurationModuleType.CAR,
536 earContext.getNaming(),
537 earContext.getConfigurationManager(),
538 null, //no server name needed on client
539 appClientModule.getAppClientName(),
540 transactionManagerObjectName,
541 connectionTrackerObjectName,
542 null,
543 null,
544 corbaGBeanObjectName,
545 earContext.getMessageDestinations());
546 appClientModule.setEarContext(appClientDeploymentContext);
547 appClientModule.setRootEarContext(appClientDeploymentContext);
548
549 try {
550 appClientDeploymentContext.addIncludeAsPackedJar(URI.create(module.getTargetPath()), moduleFile);
551 } catch (IOException e) {
552 throw new DeploymentException("Unable to copy app client module jar into configuration: " + moduleFile.getName(), e);
553 }
554 ClassPathList libClasspath = (ClassPathList) earContext.getGeneralData().get(ClassPathList.class);
555 if (libClasspath != null) {
556 for (String libEntryPath : libClasspath) {
557 try {
558 NestedJarFile library = new NestedJarFile(earFile, libEntryPath);
559 appClientDeploymentContext.addIncludeAsPackedJar(URI.create(libEntryPath), library);
560 } catch (IOException e) {
561 throw new DeploymentException("Could not add to app client library classpath: " + libEntryPath, e);
562 }
563 }
564 }
565 } catch (DeploymentException e) {
566 cleanupAppClientDir(appClientDir);
567 throw e;
568 }
569 for (ConnectorModule connectorModule : appClientModule.getResourceModules()) {
570 getConnectorModuleBuilder().installModule(connectorModule.getModuleFile(), appClientDeploymentContext, connectorModule, configurationStores, targetConfigurationStore, repositories);
571 }
572
573 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
574 mbe.installModule(module.getModuleFile(), appClientDeploymentContext, module, configurationStores, targetConfigurationStore, repositories);
575 }
576 }
577
578 public void initContext(EARContext earContext, Module clientModule, ClassLoader cl) throws DeploymentException {
579 namingBuilders.buildEnvironment(clientModule.getSpecDD(), clientModule.getVendorDD(), ((AppClientModule) clientModule).getEnvironment());
580
581 AppClientModule appClientModule = ((AppClientModule) clientModule);
582 for (ConnectorModule connectorModule : appClientModule.getResourceModules()) {
583 getConnectorModuleBuilder().initContext(appClientModule.getEarContext(), connectorModule, cl);
584 }
585 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
586 mbe.initContext(earContext, clientModule, cl);
587 }
588 }
589
590 public void addGBeans(EARContext earContext, Module module, ClassLoader earClassLoader, Collection repositories) throws DeploymentException {
591
592 AppClientModule appClientModule = (AppClientModule) module;
593 JarFile moduleFile = module.getModuleFile();
594
595 ApplicationClientType appClient = (ApplicationClientType) appClientModule.getSpecDD();
596 GerApplicationClientType geronimoAppClient = (GerApplicationClientType) appClientModule.getVendorDD();
597
598 // generate the object name for the app client
599 AbstractName appClientModuleName = appClientModule.getModuleName();
600
601 // create a gbean for the app client module and add it to the ear
602 GBeanData appClientModuleGBeanData = new GBeanData(appClientModuleName, J2EEAppClientModuleImpl.GBEAN_INFO);
603 try {
604 appClientModuleGBeanData.setReferencePattern("J2EEServer", earContext.getServerName());
605 if (!module.isStandAlone()) {
606 appClientModuleGBeanData.setReferencePattern("J2EEApplication", earContext.getModuleName());
607 }
608
609 } catch (Exception e) {
610 throw new DeploymentException("Unable to initialize AppClientModule GBean", e);
611 }
612 try {
613 earContext.addGBean(appClientModuleGBeanData);
614 } catch (GBeanAlreadyExistsException e) {
615 throw new DeploymentException("Could not add application client module gbean to configuration", e);
616 }
617
618 EARContext appClientDeploymentContext = appClientModule.getEarContext();
619 //Share the ejb info with the ear.
620 //TODO this might be too much, but I don't want to impose a dependency on geronimo-openejb to get
621 //EjbModuleBuilder.EarData.class
622 Map<Object, Object> generalData = earContext.getGeneralData();
623 for (Map.Entry<Object, Object> entry : generalData.entrySet()) {
624 Object key = entry.getKey();
625 if (key instanceof Class && ((Class) key).getName().equals("org.apache.geronimo.openejb.deployment.EjbModuleBuilder$EarData")) {
626 appClientDeploymentContext.getGeneralData().put(key, entry.getValue());
627 break;
628 }
629 }
630
631 // Create a Module ID Builder defaulting to similar settings to use for any children we create
632 ModuleIDBuilder idBuilder = new ModuleIDBuilder();
633 idBuilder.setDefaultGroup(appClientModule.getEnvironment().getConfigId().getGroupId());
634 idBuilder.setDefaultVersion(appClientModule.getEnvironment().getConfigId().getVersion());
635 try {
636 try {
637
638 //register the message destinations in the app client ear context.
639 namingBuilders.initContext(appClient, geronimoAppClient, appClientModule);
640 // extract the client Jar file into a standalone packed jar file and add the contents to the output
641 URI moduleBase = new URI(appClientModule.getTargetPath());
642 try {
643 appClientDeploymentContext.addIncludeAsPackedJar(moduleBase, moduleFile);
644 } catch (IOException e) {
645 throw new DeploymentException("Unable to copy app client module jar into configuration: " + moduleFile.getName(), e);
646 }
647
648 // add manifest class path entries to the app client context
649 addManifestClassPath(appClientDeploymentContext, appClientModule.getEarFile(), moduleFile, moduleBase);
650
651 // get the classloader
652 ClassLoader appClientClassLoader = appClientDeploymentContext.getClassLoader();
653
654 // pop in all the gbeans declared in the geronimo app client file
655 if (geronimoAppClient != null) {
656 serviceBuilder.build(geronimoAppClient, appClientDeploymentContext, appClientDeploymentContext);
657 //deploy the resource adapters specified in the geronimo-application.xml
658
659 for (ConnectorModule connectorModule : appClientModule.getResourceModules()) {
660 getConnectorModuleBuilder().addGBeans(appClientDeploymentContext, connectorModule, appClientClassLoader, repositories);
661 }
662 }
663
664 //Holder may be loaded in the "client" module classloader here, whereas
665 //NamingBuilder.INJECTION_KEY.get(buildingContext) returns a Holder loaded in the j2ee-server classloader.
666 Object holder;
667 // add the app client static jndi provider
668 //TODO track resource ref shared and app managed security
669 AbstractName jndiContextName = earContext.getNaming().createChildName(appClientDeploymentContext.getModuleName(), "StaticJndiContext", "StaticJndiContext");
670 GBeanData jndiContextGBeanData = new GBeanData(jndiContextName, StaticJndiContextPlugin.GBEAN_INFO);
671 try {
672 Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>();
673 buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, jndiContextName);
674 Configuration localConfiguration = appClientDeploymentContext.getConfiguration();
675 Configuration remoteConfiguration = earContext.getConfiguration();
676
677 if (!appClient.getMetadataComplete()) {
678 // Create a classfinder and populate it for the naming builder(s). The absence of a
679 // classFinder in the module will convey whether metadata-complete is set
680 // (or not)
681 appClientModule.setClassFinder(createAppClientClassFinder(appClient, appClientModule));
682 }
683
684 namingBuilders.buildNaming(appClient, geronimoAppClient, appClientModule, buildingContext);
685
686 if (!appClient.getMetadataComplete()) {
687 appClient.setMetadataComplete(true);
688 module.setOriginalSpecDD(module.getSpecDD().toString());
689 }
690
691 appClientModuleGBeanData.setAttribute("deploymentDescriptor", appClientModule.getOriginalSpecDD());
692 holder = NamingBuilder.INJECTION_KEY.get(buildingContext);
693 jndiContextGBeanData.setAttribute("context", NamingBuilder.JNDI_KEY.get(buildingContext));
694 } catch (DeploymentException e) {
695 throw e;
696 } catch (Exception e) {
697 throw new DeploymentException("Unable to construct jndi context for AppClientModule GBean " +
698 appClientModule.getName(), e);
699 }
700 appClientDeploymentContext.addGBean(jndiContextGBeanData);
701
702 // finally add the app client container
703 AbstractName appClientContainerName = appClientDeploymentContext.getModuleName();
704 GBeanData appClientContainerGBeanData = new GBeanData(appClientContainerName, AppClientContainer.GBEAN_INFO);
705 try {
706 appClientContainerGBeanData.setAttribute("mainClassName", appClientModule.getMainClassName());
707 appClientContainerGBeanData.setAttribute("appClientModuleName", appClientModuleName);
708 String callbackHandlerClassName = null;
709 if (appClient.isSetCallbackHandler()) {
710 callbackHandlerClassName = appClient.getCallbackHandler().getStringValue().trim();
711 }
712 if (geronimoAppClient.isSetCallbackHandler()) {
713 callbackHandlerClassName = geronimoAppClient.getCallbackHandler().trim();
714 }
715 String realmName = null;
716 if (geronimoAppClient.isSetRealmName()) {
717 realmName = geronimoAppClient.getRealmName().trim();
718 }
719 if (callbackHandlerClassName != null && realmName == null) {
720 throw new DeploymentException("You must specify a realm name with the callback handler");
721 }
722 if (realmName != null) {
723 appClientContainerGBeanData.setAttribute("realmName", realmName);
724 appClientContainerGBeanData.setAttribute("callbackHandlerClassName", callbackHandlerClassName);
725 } else if (geronimoAppClient.isSetDefaultSubject()) {
726 GerSubjectInfoType subjectInfoType = geronimoAppClient.getDefaultSubject();
727 SubjectInfo subjectInfo = buildSubjectInfo(subjectInfoType);
728 appClientContainerGBeanData.setAttribute("defaultSubject", subjectInfo);
729 appClientContainerGBeanData.setReferencePattern("CredentialStore", credentialStoreName);
730 } else if (earContext.getSecurityConfiguration() != null) {
731 //beware a linkage error if we cast this to SubjectInfo
732 String realm = ((SecurityConfiguration) earContext.getSecurityConfiguration()).getDefaultSubjectRealm();
733 String id = ((SecurityConfiguration) earContext.getSecurityConfiguration()).getDefaultSubjectId();
734 if (realm != null) {
735 SubjectInfo subjectInfo = new SubjectInfo(realm, id);
736 appClientContainerGBeanData.setAttribute("defaultSubject", subjectInfo);
737 appClientContainerGBeanData.setReferencePattern("CredentialStore", credentialStoreName);
738 }
739 }
740 appClientContainerGBeanData.setReferencePattern("JNDIContext", jndiContextName);
741 appClientContainerGBeanData.setAttribute("holder", holder);
742
743 } catch (Exception e) {
744 throw new DeploymentException("Unable to initialize AppClientModule GBean", e);
745 }
746 appClientDeploymentContext.addGBean(appClientContainerGBeanData);
747
748 //TODO this may definitely not be the best place for this!
749 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
750 mbe.addGBeans(appClientDeploymentContext, appClientModule, appClientClassLoader, repositories);
751 }
752
753 // get the configuration data
754 earContext.addAdditionalDeployment(appClientDeploymentContext.getConfigurationData());
755 } finally {
756 if (appClientDeploymentContext != null) {
757 try {
758 appClientDeploymentContext.close();
759 } catch (IOException e) {
760 //nothing we can do
761 }
762 }
763 }
764
765 } catch (Throwable e) {
766 File appClientDir = appClientDeploymentContext.getBaseDir();
767 cleanupAppClientDir(appClientDir);
768 if (e instanceof Error) {
769 throw (Error) e;
770 } else if (e instanceof DeploymentException) {
771 throw (DeploymentException) e;
772 } else if (e instanceof Exception) {
773 throw new DeploymentException(e);
774 }
775 throw new Error(e);
776 }
777 }
778
779
780 private ClassFinder createAppClientClassFinder(ApplicationClientType appClient, AppClientModule appClientModule) throws DeploymentException {
781
782 //------------------------------------------------------------------------------------
783 // Find the list of classes from the application-client.xml we want to search for
784 // annotations in
785 //------------------------------------------------------------------------------------
786 List<Class> classes = new ArrayList<Class>();
787
788 // Get the classloader from the module's EARContext
789 ClassLoader classLoader = appClientModule.getEarContext().getClassLoader();
790
791 // Get the main class from the module
792 String mainClass = appClientModule.getMainClassName();
793 Class<?> mainClas;
794 try {
795 mainClas = classLoader.loadClass(mainClass);
796 }
797 catch (ClassNotFoundException e) {
798 throw new DeploymentException("AppClientModuleBuilder: Could not load main class: " + mainClass, e);
799 }
800 while (mainClas != null && mainClas != Object.class) {
801 classes.add(mainClas);
802 mainClas = mainClas.getSuperclass();
803 }
804
805 // Get the callback-handler from the deployment descriptor
806 if (appClient.isSetCallbackHandler()) {
807 FullyQualifiedClassType cls = appClient.getCallbackHandler();
808 Class<?> clas;
809 try {
810 clas = classLoader.loadClass(cls.getStringValue().trim());
811 }
812 catch (ClassNotFoundException e) {
813 throw new DeploymentException("AppClientModuleBuilder: Could not load callback-handler class: " + cls.getStringValue(), e);
814 }
815 classes.add(clas);
816 }
817
818 return new ClassFinder(classes);
819 }
820
821 private SubjectInfo buildSubjectInfo(GerSubjectInfoType defaultSubject) {
822 String realmName = defaultSubject.getRealm().trim();
823 String id = defaultSubject.getId().trim();
824 return new SubjectInfo(realmName, id);
825 }
826
827 public String getSchemaNamespace() {
828 return GERAPPCLIENT_NAMESPACE;
829 }
830
831 public void addManifestClassPath(DeploymentContext deploymentContext, JarFile earFile, JarFile jarFile, URI jarFileLocation) throws DeploymentException {
832 Manifest manifest;
833 try {
834 manifest = jarFile.getManifest();
835 } catch (IOException e) {
836 throw new DeploymentException("Could not read manifest: " + jarFileLocation, e);
837 }
838
839 if (manifest == null) {
840 return;
841 }
842 String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
843 if (manifestClassPath == null) {
844 return;
845 }
846
847 for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
848 String path = tokenizer.nextToken();
849
850 URI pathUri;
851 try {
852 pathUri = new URI(path);
853 } catch (URISyntaxException e) {
854 throw new DeploymentException("Invalid manifest classpath entry: jarFile=" + jarFileLocation + ", path=" + path, e);
855 }
856
857 if (!pathUri.getPath().endsWith(".jar")) {
858 throw new DeploymentException("Manifest class path entries must end with the .jar extension (JAVAEE 5 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path);
859 }
860 if (pathUri.isAbsolute()) {
861 throw new DeploymentException("Manifest class path entries must be relative (JAVAEE 5 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path);
862 }
863
864 // determine the target file
865 URI classPathJarLocation = jarFileLocation.resolve(pathUri);
866 File classPathFile = deploymentContext.getTargetFile(classPathJarLocation);
867
868 // we only recuse if the path entry is not already in the output context
869 // this will work for all current cases, but may not work in the future
870 if (!classPathFile.exists()) {
871 // check if the path exists in the earFile
872 ZipEntry entry = earFile.getEntry(classPathJarLocation.getPath());
873 if (entry == null) {
874 throw new DeploymentException("Cound not find manifest class path entry: jarFile=" + jarFileLocation + ", path=" + path);
875 }
876
877 try {
878 // copy the file into the output context
879 deploymentContext.addFile(classPathJarLocation, earFile, entry);
880 } catch (IOException e) {
881 throw new DeploymentException("Cound not copy manifest class path entry into configuration: jarFile=" + jarFileLocation + ", path=" + path, e);
882 }
883
884 JarFile classPathJarFile;
885 try {
886 classPathJarFile = new JarFile(classPathFile);
887 } catch (IOException e) {
888 throw new DeploymentException("Manifest class path entries must be a valid jar file (JAVAEE 5 Section 8.2): jarFile=" + jarFileLocation + ", path=" + path, e);
889 }
890
891 // add the client jars of this class path jar
892 addManifestClassPath(deploymentContext, earFile, classPathJarFile, classPathJarLocation);
893 }
894 }
895 }
896
897 private boolean cleanupAppClientDir(File configurationDir) {
898 LinkedList<String> cannotBeDeletedList = new LinkedList<String>();
899
900 if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) {
901 // Output a message to help user track down file problem
902 log.warn("Unable to delete " + cannotBeDeletedList.size() +
903 " files while recursively deleting directory "
904 + configurationDir.getAbsolutePath() + LINE_SEP +
905 "The first file that could not be deleted was:" + LINE_SEP + " " +
906 (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : ""));
907 return false;
908 }
909 return true;
910 }
911
912 public static final GBeanInfo GBEAN_INFO;
913
914 static {
915 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(AppClientModuleBuilder.class, NameFactory.MODULE_BUILDER);
916 infoBuilder.addAttribute("defaultClientEnvironment", Environment.class, true, true);
917 infoBuilder.addAttribute("defaultServerEnvironment", Environment.class, true, true);
918 infoBuilder.addAttribute("transactionManagerObjectName", AbstractNameQuery.class, true);
919 infoBuilder.addAttribute("connectionTrackerObjectName", AbstractNameQuery.class, true);
920 infoBuilder.addAttribute("corbaGBeanObjectName", AbstractNameQuery.class, true);
921 infoBuilder.addAttribute("credentialStoreName", AbstractNameQuery.class, true);
922 infoBuilder.addReference("Repositories", Repository.class, "Repository");
923 infoBuilder.addReference("ConnectorModuleBuilder", ModuleBuilder.class, NameFactory.MODULE_BUILDER);
924 infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
925 infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
926 infoBuilder.addReference("ModuleBuilderExtensions", ModuleBuilderExtension.class, NameFactory.MODULE_BUILDER);
927 infoBuilder.addReference("ClientArtifactResolver", ArtifactResolver.class, "ArtifactResolver");
928
929 infoBuilder.addInterface(ModuleBuilder.class);
930
931 infoBuilder.setConstructor(new String[]{"transactionManagerObjectName",
932 "connectionTrackerObjectName",
933 "corbaGBeanObjectName",
934 "credentialStoreName",
935 "Repositories",
936 "ConnectorModuleBuilder",
937 "ServiceBuilders",
938 "NamingBuilders",
939 "ModuleBuilderExtensions",
940 "ClientArtifactResolver",
941 "defaultClientEnvironment",
942 "defaultServerEnvironment",
943 });
944
945 GBEAN_INFO = infoBuilder.getBeanInfo();
946 }
947
948 public static GBeanInfo getGBeanInfo() {
949 return GBEAN_INFO;
950 }
951
952 }