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 }