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 }