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