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