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.kernel.config; 018 019 import java.io.File; 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.io.PrintWriter; 024 import java.net.JarURLConnection; 025 import java.net.URI; 026 import java.net.URL; 027 import java.util.ArrayList; 028 import java.util.Collection; 029 import java.util.Collections; 030 import java.util.HashSet; 031 import java.util.Iterator; 032 import java.util.LinkedHashSet; 033 import java.util.List; 034 import java.util.Map; 035 import java.util.Properties; 036 import java.util.Set; 037 038 import org.apache.commons.logging.Log; 039 import org.apache.commons.logging.LogFactory; 040 import org.apache.geronimo.gbean.AbstractName; 041 import org.apache.geronimo.gbean.AbstractNameQuery; 042 import org.apache.geronimo.gbean.GAttributeInfo; 043 import org.apache.geronimo.gbean.GBeanData; 044 import org.apache.geronimo.gbean.GReferenceInfo; 045 import org.apache.geronimo.gbean.InvalidConfigurationException; 046 import org.apache.geronimo.gbean.ReferencePatterns; 047 import org.apache.geronimo.kernel.ClassLoading; 048 import org.apache.geronimo.kernel.GBeanAlreadyExistsException; 049 import org.apache.geronimo.kernel.GBeanNotFoundException; 050 import org.apache.geronimo.kernel.InternalKernelException; 051 import org.apache.geronimo.kernel.Kernel; 052 import org.apache.geronimo.kernel.basic.BasicKernel; 053 import org.apache.geronimo.kernel.management.State; 054 import org.apache.geronimo.kernel.repository.Artifact; 055 import org.apache.geronimo.kernel.repository.ArtifactResolver; 056 import org.apache.geronimo.kernel.repository.DefaultArtifactManager; 057 import org.apache.geronimo.kernel.repository.DefaultArtifactResolver; 058 import org.apache.geronimo.kernel.repository.Maven2Repository; 059 060 /** 061 * @version $Rev:386276 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 062 */ 063 public final class ConfigurationUtil { 064 private static final Log log = LogFactory.getLog(ConfigurationUtil.class); 065 private static final ConfigurationMarshaler configurationMarshaler; 066 067 static { 068 ConfigurationMarshaler marshaler = null; 069 String marshalerClass = System.getProperty("Xorg.apache.geronimo.kernel.config.Marshaler"); 070 if (marshalerClass != null) { 071 try { 072 marshaler = createConfigurationMarshaler(marshalerClass); 073 } catch (Exception e) { 074 log.error("Error creating configuration marshaler class " + marshalerClass , e); 075 } 076 } 077 078 // todo this code effectively makes the default format xstream 079 //if (marshaler == null) { 080 // try { 081 // marshaler = createConfigurationMarshaler("org.apache.geronimo.kernel.config.xstream.XStreamConfigurationMarshaler"); 082 // } catch (Throwable ignored) { 083 // } 084 //} 085 086 if (marshaler == null) { 087 marshaler = new SerializedConfigurationMarshaler(); 088 } 089 090 configurationMarshaler = marshaler; 091 } 092 093 private static File bootDirectory; 094 095 private static File getStartupDirectory() { 096 // guess from the location of the jar 097 URL url = ConfigurationUtil.class.getClassLoader().getResource("META-INF/startup-jar"); 098 099 File directory = null; 100 if (url != null) { 101 try { 102 JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); 103 url = jarConnection.getJarFileURL(); 104 105 URI baseURI = new URI(url.toString()).resolve(".."); 106 directory = new File(baseURI); 107 } catch (Exception ignored) { 108 log.error("Error while determining the installation directory of Apache Geronimo", ignored); 109 } 110 } else { 111 log.error("Cound not determine the installation directory of Apache Geronimo, because the startup jar could not be found in the current class loader."); 112 } 113 114 return directory; 115 } 116 117 private static File getBootDirectory() { 118 if (bootDirectory == null) { 119 bootDirectory = getStartupDirectory(); 120 } 121 return bootDirectory; 122 } 123 124 public static ConfigurationMarshaler createConfigurationMarshaler(String marshalerClass) throws Exception { 125 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 126 Class clazz = null; 127 if (classLoader != null) { 128 try { 129 clazz = ClassLoading.loadClass(marshalerClass, classLoader); 130 } catch (ClassNotFoundException ignored) { 131 // doesn't matter 132 } 133 } 134 if (clazz == null) { 135 classLoader = ConfigurationUtil.class.getClassLoader(); 136 try { 137 clazz = ClassLoading.loadClass(marshalerClass, classLoader); 138 } catch (ClassNotFoundException ignored) { 139 // doesn't matter 140 } 141 } 142 143 if (clazz != null) { 144 Object object = clazz.newInstance(); 145 if (object instanceof ConfigurationMarshaler) { 146 return (ConfigurationMarshaler) object; 147 } else { 148 log.warn("Configuration marshaler class is not an instance of ConfigurationMarshaler " + marshalerClass + ": using default configuration "); 149 } 150 } 151 return null; 152 } 153 154 private ConfigurationUtil() { 155 } 156 157 public static GBeanState newGBeanState(Collection gbeans) { 158 return configurationMarshaler.newGBeanState(gbeans); 159 } 160 161 public static AbstractName loadBootstrapConfiguration(Kernel kernel, InputStream in, ClassLoader classLoader) throws Exception { 162 return loadBootstrapConfiguration(kernel, in, classLoader, false); 163 } 164 165 public static AbstractName loadBootstrapConfiguration(Kernel kernel, InputStream in, ClassLoader classLoader, boolean enableBootRepo) throws Exception { 166 ConfigurationData configurationData = readConfigurationData(in); 167 return loadBootstrapConfiguration(kernel, configurationData, classLoader, enableBootRepo); 168 } 169 170 public static AbstractName loadBootstrapConfiguration(Kernel kernel, ConfigurationData configurationData, ClassLoader classLoader) throws Exception { 171 return loadBootstrapConfiguration(kernel, configurationData, classLoader, false); 172 } 173 174 public static AbstractName loadBootstrapConfiguration(Kernel kernel, ConfigurationData configurationData, ClassLoader classLoader, boolean enableBootRepo) throws Exception { 175 if (kernel == null) throw new NullPointerException("kernel is null"); 176 if (configurationData == null) throw new NullPointerException("configurationData is null"); 177 if (classLoader == null) throw new NullPointerException("classLoader is null"); 178 179 // build the gbean data 180 Artifact configId = configurationData.getId(); 181 AbstractName abstractName = Configuration.getConfigurationAbstractName(configId); 182 GBeanData gbeanData = new GBeanData(abstractName, Configuration.GBEAN_INFO); 183 gbeanData.setAttribute("configurationData", configurationData); 184 185 Collection repositories = null; 186 ArtifactResolver artifactResolver = null; 187 if (enableBootRepo) { 188 String repository = System.getProperty("Xorg.apache.geronimo.repository.boot.path", "repository"); 189 Maven2Repository bootRepository = new Maven2Repository(new File(getBootDirectory(), repository)); 190 repositories = Collections.singleton(bootRepository); 191 artifactResolver = new DefaultArtifactResolver(new DefaultArtifactManager(), bootRepository); 192 } else { 193 // a bootstrap configuration can not have any dependencies 194 List dependencies = configurationData.getEnvironment().getDependencies(); 195 if (!dependencies.isEmpty()) { 196 configurationData.getEnvironment().setDependencies(Collections.EMPTY_SET); 197 } 198 } 199 gbeanData.setAttribute("configurationResolver", new ConfigurationResolver(configurationData, repositories, artifactResolver)); 200 201 // load and start the gbean 202 kernel.loadGBean(gbeanData, classLoader); 203 kernel.startGBean(gbeanData.getAbstractName()); 204 205 Configuration configuration = (Configuration) kernel.getGBean(gbeanData.getAbstractName()); 206 207 // start the gbeans 208 startConfigurationGBeans(configuration.getAbstractName(), configuration, kernel); 209 210 ConfigurationManager configurationManager = getConfigurationManager(kernel); 211 configurationManager.loadConfiguration(configId); 212 return gbeanData.getAbstractName(); 213 } 214 215 public static void writeConfigurationData(ConfigurationData configurationData, OutputStream out) throws IOException { 216 configurationMarshaler.writeConfigurationData(configurationData, out); 217 } 218 219 public static ConfigurationData readConfigurationData(InputStream in) throws IOException, ClassNotFoundException { 220 return configurationMarshaler.readConfigurationData(in); 221 } 222 223 public static void writeConfigInfo(PrintWriter writer, ConfigurationData configurationData) { 224 writeConfigInfo("", writer, configurationData); 225 } 226 227 private static void writeConfigInfo(String prefix, PrintWriter writer, ConfigurationData configurationData) { 228 writer.println(prefix+"id=" + configurationData.getId()); 229 writer.println(prefix+"type=" + configurationData.getModuleType()); 230 writer.println(prefix+"created=" + configurationData.getCreated()); 231 Set<Artifact> ownedConfigurations = configurationData.getOwnedConfigurations(); 232 int i = 0; 233 for (Artifact ownedConfiguration : ownedConfigurations) { 234 writer.println(prefix + "owned." + i++ + "=" + ownedConfiguration); 235 } 236 i = 0; 237 for (ConfigurationData data : configurationData.getChildConfigurations().values()) { 238 writeConfigInfo("child." + i++ + ".", writer, data); 239 } 240 writer.flush(); 241 } 242 243 public static ConfigurationInfo readConfigurationInfo(InputStream in, AbstractName storeName, File inPlaceLocation) throws IOException { 244 Properties properties = new Properties(); 245 properties.load(in); 246 return readConfigurationInfo("", properties, storeName, inPlaceLocation); 247 } 248 249 private static ConfigurationInfo readConfigurationInfo(String prefix, Properties properties, AbstractName storeName, File inPlaceLocation) throws IOException { 250 String id = properties.getProperty(prefix+"id"); 251 Artifact configId = Artifact.create(id); 252 253 String type = properties.getProperty(prefix+"type"); 254 ConfigurationModuleType moduleType = ConfigurationModuleType.getByName(type); 255 if (moduleType == null) { 256 throw new IllegalArgumentException("Unknown module type: " + type); 257 } 258 259 String created = properties.getProperty(prefix+"created"); 260 long time; 261 try { 262 time = Long.parseLong(created); 263 } catch (NumberFormatException e) { 264 throw new IllegalArgumentException("Invalid created time: " + created); 265 } 266 267 LinkedHashSet ownedConfigurations = new LinkedHashSet(); 268 for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) { 269 Map.Entry entry = (Map.Entry) iterator.next(); 270 String name = (String) entry.getKey(); 271 if (name.startsWith(prefix+"owned.")) { 272 String value = (String) entry.getValue(); 273 Artifact ownedConfiguration = Artifact.create(value); 274 ownedConfigurations.add(ownedConfiguration); 275 } 276 } 277 LinkedHashSet childConfigurations = new LinkedHashSet(); 278 int test = 0; 279 while(true) { 280 String next = prefix+"child."+test+"."; 281 String value = properties.getProperty(next+".id"); 282 if(value == null) { 283 break; 284 } 285 childConfigurations.add(readConfigurationInfo(next, properties, storeName, inPlaceLocation)); 286 ++test; 287 } 288 289 return new ConfigurationInfo(storeName, configId, moduleType, time, ownedConfigurations, childConfigurations, inPlaceLocation); 290 } 291 292 /** 293 * Gets the name of the ConfigurationManager running in the specified kernel. 294 * 295 * @return Its AbstractName 296 * @throws IllegalStateException Occurs if a ConfigurationManager cannot be identified 297 */ 298 public static AbstractName getConfigurationManagerName(Kernel kernel) { 299 Set names = kernel.listGBeans(new AbstractNameQuery(ConfigurationManager.class.getName())); 300 for (Iterator iterator = names.iterator(); iterator.hasNext();) { 301 AbstractName abstractName = (AbstractName) iterator.next(); 302 if (!kernel.isRunning(abstractName)) { 303 iterator.remove(); 304 } 305 } 306 if (names.isEmpty()) { 307 throw new IllegalStateException("A Configuration Manager could not be found in the kernel"); 308 } 309 if (names.size() > 1) { 310 String error = "More than one Configuration Manager was found in the kernel: "; 311 for (Object name : names) { 312 AbstractName abName = (AbstractName)name; 313 error = error + "\"" + abName.toString() + "\" "; 314 } 315 throw new IllegalStateException(error); 316 } 317 return (AbstractName) names.iterator().next(); 318 } 319 320 321 /** 322 * Gets a reference or proxy to the ConfigurationManager running in the specified kernel. 323 * 324 * @return The ConfigurationManager 325 * @throws IllegalStateException Occurs if a ConfigurationManager cannot be identified 326 */ 327 public static ConfigurationManager getConfigurationManager(Kernel kernel) { 328 AbstractName configurationManagerName = getConfigurationManagerName(kernel); 329 return (ConfigurationManager) kernel.getProxyManager().createProxy(configurationManagerName, ConfigurationManager.class); 330 } 331 332 /** 333 * Gets a reference or proxy to an EditableConfigurationManager running in the specified kernel, if there is one. 334 * 335 * @return The EdtiableConfigurationManager, or none if there is not one available. 336 * @throws IllegalStateException Occurs if there are multiple EditableConfigurationManagers in the kernel. 337 */ 338 public static EditableConfigurationManager getEditableConfigurationManager(Kernel kernel) { 339 Set names = kernel.listGBeans(new AbstractNameQuery(EditableConfigurationManager.class.getName())); 340 for (Iterator iterator = names.iterator(); iterator.hasNext();) { 341 AbstractName abstractName = (AbstractName) iterator.next(); 342 if (!kernel.isRunning(abstractName)) { 343 iterator.remove(); 344 } 345 } 346 if (names.isEmpty()) { 347 return null; // may be one, just not editable 348 } 349 if (names.size() > 1) { 350 throw new IllegalStateException("More than one Configuration Manager was found in the kernel"); 351 } 352 AbstractName configurationManagerName = (AbstractName) names.iterator().next(); 353 return (EditableConfigurationManager) kernel.getProxyManager().createProxy(configurationManagerName, EditableConfigurationManager.class); 354 } 355 356 public static void releaseConfigurationManager(Kernel kernel, ConfigurationManager configurationManager) { 357 kernel.getProxyManager().destroyProxy(configurationManager); 358 } 359 360 static void preprocessGBeanData(AbstractName configurationName, Configuration configuration, GBeanData gbeanData) throws InvalidConfigException { 361 if (log.isDebugEnabled()) { 362 log.debug("resolving dependencies for " + gbeanData.getAbstractName()); 363 } 364 for (Iterator references = gbeanData.getReferencesNames().iterator(); references.hasNext();) { 365 String referenceName = (String) references.next(); 366 GReferenceInfo referenceInfo = gbeanData.getGBeanInfo().getReference(referenceName); 367 if (referenceInfo == null) { 368 throw new InvalidConfigException("No reference named " + referenceName + " in gbean " + gbeanData.getAbstractName()); 369 } 370 boolean isSingleValued = !referenceInfo.getProxyType().equals(Collection.class.getName()); 371 if (isSingleValued) { 372 ReferencePatterns referencePatterns = gbeanData.getReferencePatterns(referenceName); 373 AbstractName abstractName; 374 try { 375 abstractName = configuration.findGBean(referencePatterns); 376 if (log.isDebugEnabled()) { 377 log.debug("referencePatterns: " + referencePatterns + " resolved to " + abstractName); 378 } 379 } catch (GBeanNotFoundException e) { 380 throw new InvalidConfigException("Unable to resolve reference \"" + referenceName + "\" in gbean " + gbeanData.getAbstractName() + " to a gbean matching the pattern " + referencePatterns, e); 381 } 382 gbeanData.setReferencePatterns(referenceName, new ReferencePatterns(abstractName)); 383 } 384 } 385 386 Set newDependencies = new HashSet(); 387 for (Iterator dependencyIterator = gbeanData.getDependencies().iterator(); dependencyIterator.hasNext();) { 388 ReferencePatterns referencePatterns = (ReferencePatterns) dependencyIterator.next(); 389 AbstractName abstractName; 390 try { 391 abstractName = configuration.findGBean(referencePatterns); 392 if (log.isDebugEnabled()) { 393 log.debug("referencePatterns: " + referencePatterns + " resolved to " + abstractName); 394 } 395 } catch (GBeanNotFoundException e) { 396 throw new InvalidConfigException("Unable to resolve dependency in gbean " + gbeanData.getAbstractName(), e); 397 } 398 newDependencies.add(new ReferencePatterns(abstractName)); 399 } 400 gbeanData.setDependencies(newDependencies); 401 402 // If the GBean has a configurationBaseUrl attribute, set it 403 // todo Even though this is not used by the classloader, web apps still need this. WHY??? 404 GAttributeInfo attribute = gbeanData.getGBeanInfo().getAttribute("configurationBaseUrl"); 405 if (attribute != null && attribute.getType().equals("java.net.URL")) { 406 try { 407 Set set = configuration.getConfigurationResolver().resolve(""); 408 if (set.size() != 1) { 409 throw new AssertionError("Expected one match for pattern \".\", but got " + set.size() + " matches"); 410 } 411 URL baseURL = (URL) set.iterator().next(); 412 gbeanData.setAttribute("configurationBaseUrl", baseURL); 413 } catch (Exception e) { 414 throw new InvalidConfigException("Unable to set attribute named " + "configurationBaseUrl" + " in gbean " + gbeanData.getAbstractName(), e); 415 } 416 } 417 418 // add a dependency from the gbean to the configuration 419 gbeanData.addDependency(configurationName); 420 } 421 422 static void startConfigurationGBeans(AbstractName configurationName, Configuration configuration, Kernel kernel) throws InvalidConfigException { 423 List gbeans = new ArrayList(configuration.getGBeans().values()); 424 Collections.sort(gbeans, new GBeanData.PriorityComparator()); 425 426 List loaded = new ArrayList(gbeans.size()); 427 List started = new ArrayList(gbeans.size()); 428 429 try { 430 // register all the GBeans 431 for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) { 432 GBeanData gbeanData = (GBeanData) iterator.next(); 433 434 // copy the gbeanData object as not to mutate the original 435 gbeanData = new GBeanData(gbeanData); 436 437 // preprocess the gbeanData (resolve references, set base url, declare dependency, etc.) 438 preprocessGBeanData(configurationName, configuration, gbeanData); 439 440 try { 441 kernel.loadGBean(gbeanData, configuration.getConfigurationClassLoader()); 442 loaded.add(gbeanData.getAbstractName()); 443 } catch (GBeanAlreadyExistsException e) { 444 throw new InvalidConfigException(e); 445 } catch (Throwable e) { 446 log.warn("Could not load gbean " + gbeanData.getAbstractName(), e); 447 throw e; 448 } 449 } 450 451 try { 452 // start the gbeans 453 for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) { 454 GBeanData gbeanData = (GBeanData) iterator.next(); 455 AbstractName gbeanName = gbeanData.getAbstractName(); 456 kernel.startRecursiveGBean(gbeanName); 457 started.add(gbeanName); 458 } 459 460 // assure all of the gbeans are started 461 List unstarted = new ArrayList(); 462 for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) { 463 GBeanData gbeanData = (GBeanData) iterator.next(); 464 AbstractName gbeanName = gbeanData.getAbstractName(); 465 if (State.RUNNING_INDEX != kernel.getGBeanState(gbeanName)) { 466 String stateReason = null; 467 if (kernel instanceof BasicKernel) { 468 stateReason = ((BasicKernel) kernel).getStateReason(gbeanName); 469 } 470 String name = gbeanName.toURI().getQuery(); 471 if (stateReason != null) { 472 unstarted.add("The service " + name + " did not start because " + stateReason); 473 } else { 474 unstarted.add("The service " + name + " did not start for an unknown reason"); 475 } 476 } 477 } 478 if (!unstarted.isEmpty()) { 479 StringBuffer message = new StringBuffer(); 480 message.append("Configuration ").append(configuration.getId()).append(" failed to start due to the following reasons:\n"); 481 for (Iterator iterator = unstarted.iterator(); iterator.hasNext();) { 482 String reason = (String) iterator.next(); 483 message.append(" ").append(reason).append("\n"); 484 } 485 throw new InvalidConfigurationException(message.toString()); 486 } 487 } catch (GBeanNotFoundException e) { 488 throw new InvalidConfigException(e); 489 } 490 491 for (Iterator iterator = configuration.getChildren().iterator(); iterator.hasNext();) { 492 Configuration childConfiguration = (Configuration) iterator.next(); 493 ConfigurationUtil.startConfigurationGBeans(configurationName, childConfiguration, kernel); 494 } 495 } catch (Throwable e) { 496 for (Iterator iterator = started.iterator(); iterator.hasNext();) { 497 AbstractName gbeanName = (AbstractName) iterator.next(); 498 try { 499 kernel.stopGBean(gbeanName); 500 } catch (GBeanNotFoundException ignored) { 501 } catch (IllegalStateException ignored) { 502 } catch (InternalKernelException kernelException) { 503 log.debug("Error cleaning up after failed start of configuration " + configuration.getId() + " gbean " + gbeanName, kernelException); 504 } 505 } 506 for (Iterator iterator = loaded.iterator(); iterator.hasNext();) { 507 AbstractName gbeanName = (AbstractName) iterator.next(); 508 try { 509 kernel.unloadGBean(gbeanName); 510 } catch (GBeanNotFoundException ignored) { 511 } catch (IllegalStateException ignored) { 512 } catch (InternalKernelException kernelException) { 513 log.debug("Error cleaning up after failed start of configuration " + configuration.getId() + " gbean " + gbeanName, kernelException); 514 } 515 } 516 if (e instanceof Error) { 517 throw (Error) e; 518 } 519 if (e instanceof InvalidConfigException) { 520 throw (InvalidConfigException) e; 521 } 522 throw new InvalidConfigException("Unknown start exception", e); 523 } 524 } 525 }