001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 018 package org.apache.geronimo.deployment; 019 020 import java.io.ByteArrayInputStream; 021 import java.io.File; 022 import java.io.FileOutputStream; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.io.OutputStream; 026 import java.net.URI; 027 import java.net.URISyntaxException; 028 import java.net.URL; 029 import java.util.ArrayList; 030 import java.util.Collection; 031 import java.util.HashSet; 032 import java.util.Iterator; 033 import java.util.LinkedHashMap; 034 import java.util.LinkedHashSet; 035 import java.util.List; 036 import java.util.Map; 037 import java.util.Set; 038 import java.util.StringTokenizer; 039 import java.util.Collections; 040 import java.util.jar.Attributes; 041 import java.util.jar.JarFile; 042 import java.util.jar.Manifest; 043 import java.util.zip.ZipEntry; 044 import java.util.zip.ZipFile; 045 046 import org.apache.geronimo.common.DeploymentException; 047 import org.apache.geronimo.deployment.util.DeploymentUtil; 048 import org.apache.geronimo.gbean.AbstractName; 049 import org.apache.geronimo.gbean.AbstractNameQuery; 050 import org.apache.geronimo.gbean.GBeanData; 051 import org.apache.geronimo.gbean.GBeanInfo; 052 import org.apache.geronimo.gbean.ReferencePatterns; 053 import org.apache.geronimo.gbean.GReferenceInfo; 054 import org.apache.geronimo.kernel.GBeanAlreadyExistsException; 055 import org.apache.geronimo.kernel.GBeanNotFoundException; 056 import org.apache.geronimo.kernel.Naming; 057 import org.apache.geronimo.kernel.config.Configuration; 058 import org.apache.geronimo.kernel.config.ConfigurationData; 059 import org.apache.geronimo.kernel.config.ConfigurationManager; 060 import org.apache.geronimo.kernel.config.ConfigurationModuleType; 061 import org.apache.geronimo.kernel.config.NoSuchConfigException; 062 import org.apache.geronimo.kernel.repository.Artifact; 063 import org.apache.geronimo.kernel.repository.Environment; 064 065 /** 066 * @version $Rev:385232 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $ 067 */ 068 public class DeploymentContext { 069 private final File baseDir; 070 private final File inPlaceConfigurationDir; 071 private final ResourceContext resourceContext; 072 private final byte[] buffer = new byte[4096]; 073 private final Map childConfigurationDatas = new LinkedHashMap(); 074 private final ConfigurationManager configurationManager; 075 private final Configuration configuration; 076 private final Naming naming; 077 private final List additionalDeployment = new ArrayList(); 078 protected final AbstractName moduleName; 079 080 public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager, Collection repositories) throws DeploymentException { 081 this(baseDir, inPlaceConfigurationDir, environment, moduleName, moduleType, naming, createConfigurationManager(configurationManager, repositories)); 082 } 083 084 public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager) throws DeploymentException { 085 if (baseDir == null) throw new NullPointerException("baseDir is null"); 086 if (environment == null) throw new NullPointerException("environment is null"); 087 if (moduleType == null) throw new NullPointerException("type is null"); 088 if (configurationManager == null) throw new NullPointerException("configurationManager is null"); 089 090 if (!baseDir.exists()) { 091 baseDir.mkdirs(); 092 } 093 this.baseDir = baseDir; 094 095 this.inPlaceConfigurationDir = inPlaceConfigurationDir; 096 097 this.moduleName = moduleName; 098 099 this.naming = naming; 100 101 this.configuration = createTempConfiguration(environment, moduleType, baseDir, inPlaceConfigurationDir, configurationManager, naming); 102 103 this.configurationManager = configurationManager; 104 105 if (null == inPlaceConfigurationDir) { 106 resourceContext = new CopyResourceContext(configuration, baseDir); 107 } else { 108 resourceContext = new InPlaceResourceContext(configuration, inPlaceConfigurationDir); 109 } 110 } 111 112 private static ConfigurationManager createConfigurationManager(ConfigurationManager configurationManager, Collection repositories) { 113 return new DeploymentConfigurationManager(configurationManager, repositories); 114 } 115 116 private static Configuration createTempConfiguration(Environment environment, ConfigurationModuleType moduleType, File baseDir, File inPlaceConfigurationDir, ConfigurationManager configurationManager, Naming naming) throws DeploymentException { 117 try { 118 configurationManager.loadConfiguration(new ConfigurationData(moduleType, null, null, null, environment, baseDir, inPlaceConfigurationDir, naming)); 119 return configurationManager.getConfiguration(environment.getConfigId()); 120 } catch (Exception e) { 121 throw new DeploymentException("Unable to create configuration for deployment", e); 122 } 123 } 124 125 public ConfigurationManager getConfigurationManager() { 126 return configurationManager; 127 } 128 129 public Artifact getConfigID() { 130 return configuration.getId(); 131 } 132 133 public File getBaseDir() { 134 return baseDir; 135 } 136 137 public File getInPlaceConfigurationDir() { 138 return inPlaceConfigurationDir; 139 } 140 141 public Naming getNaming() { 142 return naming; 143 } 144 145 public GBeanData addGBean(String name, GBeanInfo gbeanInfo) throws GBeanAlreadyExistsException { 146 if (name == null) throw new NullPointerException("name is null"); 147 if (gbeanInfo == null) throw new NullPointerException("gbean is null"); 148 GBeanData gbean = new GBeanData(gbeanInfo); 149 configuration.addGBean(name, gbean); 150 return gbean; 151 } 152 153 public void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException { 154 if (gbean == null) throw new NullPointerException("gbean is null"); 155 if (gbean.getAbstractName() == null) throw new NullPointerException("gbean.getAbstractName() is null"); 156 configuration.addGBean(gbean); 157 } 158 159 public void removeGBean(AbstractName name) throws GBeanNotFoundException { 160 if (name == null) throw new NullPointerException("name is null"); 161 configuration.removeGBean(name); 162 } 163 164 public Set getGBeanNames() { 165 return new HashSet(configuration.getGBeans().keySet()); 166 } 167 168 /** 169 * @deprecated use findGBeans(pattern) 170 */ 171 public Set listGBeans(AbstractNameQuery pattern) { 172 return findGBeans(pattern); 173 } 174 175 public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException { 176 return configuration.findGBean(pattern); 177 } 178 179 public AbstractName findGBean(Set patterns) throws GBeanNotFoundException { 180 return configuration.findGBean(patterns); 181 } 182 183 public LinkedHashSet findGBeans(AbstractNameQuery pattern) { 184 return configuration.findGBeans(pattern); 185 } 186 187 public LinkedHashSet findGBeans(Set patterns) { 188 return configuration.findGBeans(patterns); 189 } 190 191 public GBeanData getGBeanInstance(AbstractName name) throws GBeanNotFoundException { 192 Map gbeans = configuration.getGBeans(); 193 GBeanData gbeanData = (GBeanData) gbeans.get(name); 194 if (gbeanData == null) { 195 throw new GBeanNotFoundException(name); 196 } 197 return gbeanData; 198 } 199 200 /** 201 * Add a packed jar file into the deployment context and place it into the 202 * path specified in the target path. The newly added packed jar is added 203 * to the classpath of the configuration. 204 * 205 * @param targetPath where the packed jar file should be placed 206 * @param jarFile the jar file to copy 207 * @throws IOException if there's a problem copying the jar file 208 */ 209 public void addIncludeAsPackedJar(URI targetPath, JarFile jarFile) throws IOException { 210 resourceContext.addIncludeAsPackedJar(targetPath, jarFile); 211 } 212 213 /** 214 * Add a ZIP file entry into the deployment context and place it into the 215 * path specified in the target path. The newly added entry is added 216 * to the classpath of the configuration. 217 * 218 * @param targetPath where the ZIP file entry should be placed 219 * @param zipFile the ZIP file 220 * @param zipEntry the ZIP file entry 221 * @throws IOException if there's a problem copying the ZIP entry 222 */ 223 public void addInclude(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException { 224 resourceContext.addInclude(targetPath, zipFile, zipEntry); 225 } 226 227 /** 228 * Add a file into the deployment context and place it into the 229 * path specified in the target path. The newly added file is added 230 * to the classpath of the configuration. 231 * 232 * @param targetPath where the file should be placed 233 * @param source the URL of file to be copied 234 * @throws IOException if there's a problem copying the ZIP entry 235 */ 236 public void addInclude(URI targetPath, URL source) throws IOException { 237 resourceContext.addInclude(targetPath, source); 238 } 239 240 /** 241 * Add a file into the deployment context and place it into the 242 * path specified in the target path. The newly added file is added 243 * to the classpath of the configuration. 244 * 245 * @param targetPath where the file should be placed 246 * @param source the file to be copied 247 * @throws IOException if there's a problem copying the ZIP entry 248 */ 249 public void addInclude(URI targetPath, File source) throws IOException { 250 resourceContext.addInclude(targetPath, source); 251 } 252 253 /** 254 * Import the classpath from a jar file's manifest. The imported classpath 255 * is crafted relative to <code>moduleBaseUri</code>. 256 * 257 * @param moduleFile the jar file from which the manifest is obtained. 258 * @param moduleBaseUri the base for the imported classpath 259 * @throws DeploymentException if there is a problem with the classpath in 260 * the manifest 261 */ 262 public void addManifestClassPath(JarFile moduleFile, URI moduleBaseUri) throws DeploymentException { 263 Manifest manifest; 264 try { 265 manifest = moduleFile.getManifest(); 266 } catch (IOException e) { 267 throw new DeploymentException("Could not read manifest: " + moduleBaseUri); 268 } 269 270 if (manifest == null) { 271 return; 272 } 273 String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH); 274 if (manifestClassPath == null) { 275 return; 276 } 277 278 for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) { 279 String path = tokenizer.nextToken(); 280 281 URI pathUri; 282 try { 283 pathUri = new URI(path); 284 } catch (URISyntaxException e) { 285 throw new DeploymentException("Invalid manifest classpath entry: module=" + moduleBaseUri + ", path=" + path); 286 } 287 288 if (!pathUri.getPath().endsWith(".jar")) { 289 throw new DeploymentException("Manifest class path entries must end with the .jar extension (J2EE 1.4 Section 8.2): module=" + moduleBaseUri); 290 } 291 if (pathUri.isAbsolute()) { 292 throw new DeploymentException("Manifest class path entries must be relative (J2EE 1.4 Section 8.2): moduel=" + moduleBaseUri); 293 } 294 295 try { 296 URI targetUri = moduleBaseUri.resolve(pathUri); 297 if (targetUri.getPath().endsWith("/")) throw new IllegalStateException("target path must not end with a '/' character: " + targetUri); 298 configuration.addToClassPath(targetUri.toString()); 299 } catch (IOException e) { 300 throw new DeploymentException(e); 301 } 302 } 303 } 304 305 public void addClass(URI targetPath, String fqcn, byte[] bytes) throws IOException, URISyntaxException { 306 if (!targetPath.getPath().endsWith("/")) throw new IllegalStateException("target path must end with a '/' character: " + targetPath); 307 308 String classFileName = fqcn.replace('.', '/') + ".class"; 309 310 File targetFile = getTargetFile(new URI(targetPath.toString() + classFileName)); 311 addFile(targetFile, new ByteArrayInputStream(bytes)); 312 313 configuration.addToClassPath(targetPath.toString()); 314 } 315 316 public void addFile(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException { 317 resourceContext.addFile(targetPath, zipFile, zipEntry); 318 } 319 320 public void addFile(URI targetPath, URL source) throws IOException { 321 resourceContext.addFile(targetPath, source); 322 } 323 324 public void addFile(URI targetPath, File source) throws IOException { 325 resourceContext.addFile(targetPath, source); 326 } 327 328 public void addFile(URI targetPath, String source) throws IOException { 329 resourceContext.addFile(targetPath, source); 330 } 331 332 private void addFile(File targetFile, InputStream source) throws IOException { 333 targetFile.getParentFile().mkdirs(); 334 OutputStream out = null; 335 try { 336 out = new FileOutputStream(targetFile); 337 int count; 338 while ((count = source.read(buffer)) > 0) { 339 out.write(buffer, 0, count); 340 } 341 } finally { 342 DeploymentUtil.close(out); 343 } 344 } 345 346 public File getTargetFile(URI targetPath) { 347 return resourceContext.getTargetFile(targetPath); 348 } 349 350 public ClassLoader getClassLoader() throws DeploymentException { 351 return configuration.getConfigurationClassLoader(); 352 } 353 354 public Configuration getConfiguration() { 355 return configuration; 356 } 357 358 public void flush() throws IOException{ 359 resourceContext.flush(); 360 } 361 362 public void close() throws IOException, DeploymentException { 363 if (configurationManager != null) { 364 try { 365 configurationManager.unloadConfiguration(configuration.getId()); 366 } catch (NoSuchConfigException ignored) { 367 //ignore 368 } 369 } 370 } 371 372 public void addChildConfiguration(String moduleName, ConfigurationData configurationData) { 373 childConfigurationDatas.put(moduleName, configurationData); 374 } 375 376 public ConfigurationData getConfigurationData() throws DeploymentException { 377 List failures = verify(); 378 if (!failures.isEmpty()) { 379 StringBuffer message = new StringBuffer(); 380 for (Iterator iterator = failures.iterator(); iterator.hasNext();) { 381 String failure = (String) iterator.next(); 382 if (message.length() > 0) message.append("\n"); 383 message.append(failure); 384 } 385 throw new DeploymentException(message.toString()); 386 } 387 388 ArrayList gbeans = new ArrayList(configuration.getGBeans().values()); 389 Collections.sort(gbeans, new GBeanData.PriorityComparator()); 390 ConfigurationData configurationData = new ConfigurationData(configuration.getModuleType(), 391 new LinkedHashSet(configuration.getClassPath()), 392 gbeans, 393 childConfigurationDatas, 394 configuration.getEnvironment(), 395 baseDir, 396 inPlaceConfigurationDir, 397 naming); 398 399 for (Iterator iterator = additionalDeployment.iterator(); iterator.hasNext();) { 400 ConfigurationData ownedConfiguration = (ConfigurationData) iterator.next(); 401 configurationData.addOwnedConfigurations(ownedConfiguration.getId()); 402 } 403 404 return configurationData; 405 } 406 407 public void addAdditionalDeployment(ConfigurationData configurationData) { 408 additionalDeployment.add(configurationData); 409 } 410 411 public List getAdditionalDeployment() { 412 return additionalDeployment; 413 } 414 415 public AbstractName getModuleName() { 416 return moduleName; 417 } 418 419 public List verify() throws DeploymentException { 420 List failures = new ArrayList(); 421 for (Iterator iterator = configuration.getGBeans().entrySet().iterator(); iterator.hasNext();) { 422 Map.Entry entry = (Map.Entry) iterator.next(); 423 AbstractName name = (AbstractName) entry.getKey(); 424 GBeanData gbean = (GBeanData) entry.getValue(); 425 426 for (Iterator iterator1 = gbean.getReferences().entrySet().iterator(); iterator1.hasNext();) { 427 Map.Entry referenceEntry = (Map.Entry) iterator1.next(); 428 String referenceName = (String) referenceEntry.getKey(); 429 ReferencePatterns referencePatterns = (ReferencePatterns) referenceEntry.getValue(); 430 431 String failure = verifyReference(gbean, referenceName, referencePatterns); 432 if (failure != null) { 433 failures.add(failure); 434 } 435 } 436 437 for (Iterator iterator1 = gbean.getDependencies().iterator(); iterator1.hasNext();) { 438 ReferencePatterns referencePatterns = (ReferencePatterns) iterator1.next(); 439 String failure = verifyDependency(name, referencePatterns); 440 if (failure != null) { 441 failures.add(failure); 442 } 443 } 444 } 445 return failures; 446 } 447 448 private String verifyReference(GBeanData gbean, String referenceName, ReferencePatterns referencePatterns) { 449 GReferenceInfo referenceInfo = gbean.getGBeanInfo().getReference(referenceName); 450 451 // if there is no reference info we can't verify 452 if (referenceInfo == null) return null; 453 454 // A collection valued reference doesn't need to be verified 455 if (referenceInfo.getProxyType().equals(Collection.class.getName())) return null; 456 457 if (!isVerifyReference(referencePatterns)) { 458 return "Unable to resolve reference \"" + referenceName + "\" in gbean " + 459 gbean.getAbstractName() + " to a gbean matching the pattern " + referencePatterns.getPatterns(); 460 } 461 return null; 462 } 463 464 private String verifyDependency(AbstractName name, ReferencePatterns referencePatterns) { 465 if (!isVerifyReference(referencePatterns)) { 466 return "Unable to resolve dependency in gbean " + name + 467 " to a gbean matching the pattern " + referencePatterns.getPatterns(); 468 } 469 470 return null; 471 } 472 473 private boolean isVerifyReference(ReferencePatterns referencePatterns) { 474 // we can't verify a resolved reference since it will have a specific artifact already set... 475 // hopefully the deployer won't generate bad resolved references 476 if (referencePatterns.isResolved()) return true; 477 478 // Do not verify the reference if it has an explicit depenency on another artifact, because it it likely 479 // that the other artifact is not in the "environment" (if it were you wouldn't use the long form) 480 Set patterns = referencePatterns.getPatterns(); 481 for (Iterator iterator = patterns.iterator(); iterator.hasNext();) { 482 AbstractNameQuery query = (AbstractNameQuery) iterator.next(); 483 if (query.getArtifact() != null) return true; 484 } 485 486 // attempt to find the bean 487 try { 488 findGBean(patterns); 489 return true; 490 } catch (GBeanNotFoundException e) { 491 return false; 492 } 493 } 494 }