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 }