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