View Javadoc

1   /**
2    *
3    * Copyright 2005 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.kernel.config;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.io.PrintWriter;
23  import java.io.File;
24  import java.net.URL;
25  import java.util.Collection;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.Set;
29  import java.util.List;
30  import java.util.Collections;
31  import java.util.ArrayList;
32  import java.util.Properties;
33  import java.util.Map;
34  import java.util.LinkedHashSet;
35  
36  import org.apache.geronimo.gbean.AbstractName;
37  import org.apache.geronimo.gbean.AbstractNameQuery;
38  import org.apache.geronimo.gbean.GAttributeInfo;
39  import org.apache.geronimo.gbean.GBeanData;
40  import org.apache.geronimo.gbean.GReferenceInfo;
41  import org.apache.geronimo.gbean.InvalidConfigurationException;
42  import org.apache.geronimo.gbean.ReferencePatterns;
43  import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
44  import org.apache.geronimo.kernel.GBeanNotFoundException;
45  import org.apache.geronimo.kernel.Kernel;
46  import org.apache.geronimo.kernel.ClassLoading;
47  import org.apache.geronimo.kernel.InternalKernelException;
48  import org.apache.geronimo.kernel.basic.BasicKernel;
49  import org.apache.geronimo.kernel.management.State;
50  import org.apache.geronimo.kernel.repository.Artifact;
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  
54  /**
55   * @version $Rev:386276 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $
56   */
57  public final class ConfigurationUtil {
58      private static final Log log = LogFactory.getLog(ConfigurationUtil.class);
59      private static final ConfigurationMarshaler configurationMarshaler;
60  
61      static {
62          ConfigurationMarshaler marshaler = null;
63          String marshalerClass = System.getProperty("Xorg.apache.geronimo.kernel.config.Marshaler");
64          if (marshalerClass != null) {
65              try {
66                  marshaler = createConfigurationMarshaler(marshalerClass);
67              } catch (Exception e) {
68                  log.error("Error creating configuration marshaler class " + marshalerClass , e);
69              }
70          }
71  
72          // todo this code effectively makes the default format xstream
73          //if (marshaler == null) {
74          //    try {
75          //        marshaler = createConfigurationMarshaler("org.apache.geronimo.kernel.config.xstream.XStreamConfigurationMarshaler");
76          //    } catch (Throwable ignored) {
77          //    }
78          //}
79  
80          if (marshaler == null) {
81              marshaler = new SerializedConfigurationMarshaler();
82          }
83  
84          configurationMarshaler = marshaler;
85      }
86  
87      public static ConfigurationMarshaler createConfigurationMarshaler(String marshalerClass) throws Exception {
88          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
89          Class clazz = null;
90          if (classLoader != null) {
91              try {
92                  clazz = ClassLoading.loadClass(marshalerClass, classLoader);
93              } catch (ClassNotFoundException ignored) {
94                  // doesn't matter
95              }
96          }
97          if (clazz == null) {
98              classLoader = ConfigurationUtil.class.getClassLoader();
99              try {
100                 clazz = ClassLoading.loadClass(marshalerClass, classLoader);
101             } catch (ClassNotFoundException ignored) {
102                 // doesn't matter
103             }
104         }
105 
106         if (clazz != null) {
107             Object object = clazz.newInstance();
108             if (object instanceof ConfigurationMarshaler) {
109                 return (ConfigurationMarshaler) object;
110             } else {
111                 log.warn("Configuration marshaler class is not an instance of ConfigurationMarshaler " + marshalerClass + ": using default configuration ");
112             }
113         }
114         return null;
115     }
116 
117     private ConfigurationUtil() {
118     }
119 
120     public static GBeanState newGBeanState(Collection gbeans) {
121         return configurationMarshaler.newGBeanState(gbeans);
122     }
123 
124     public static AbstractName loadBootstrapConfiguration(Kernel kernel, InputStream in, ClassLoader classLoader) throws Exception {
125         ConfigurationData configurationData = readConfigurationData(in);
126         return loadBootstrapConfiguration(kernel, configurationData, classLoader);
127     }
128 
129     public static AbstractName loadBootstrapConfiguration(Kernel kernel, ConfigurationData configurationData, ClassLoader classLoader) throws Exception {
130         if (kernel == null) throw new NullPointerException("kernel is null");
131         if (configurationData == null) throw new NullPointerException("configurationData is null");
132         if (classLoader == null) throw new NullPointerException("classLoader is null");
133 
134         // a bootstrap configuration can not have any dependencies
135         List dependencies = configurationData.getEnvironment().getDependencies();
136         if (!dependencies.isEmpty()) {
137             configurationData.getEnvironment().setDependencies(Collections.EMPTY_SET);
138 //            throw new InvalidConfigurationException("Booststrap configuration can not have dependendencies: " + dependencies);
139         }
140 
141         // build the gbean data
142         Artifact configId = configurationData.getId();
143         AbstractName abstractName = Configuration.getConfigurationAbstractName(configId);
144         GBeanData gbeanData = new GBeanData(abstractName, Configuration.GBEAN_INFO);
145         gbeanData.setAttribute("configurationData", configurationData);
146         gbeanData.setAttribute("configurationResolver", new ConfigurationResolver(configurationData, null, null));
147 
148         // load and start the gbean
149         kernel.loadGBean(gbeanData, classLoader);
150         kernel.startGBean(gbeanData.getAbstractName());
151 
152         Configuration configuration = (Configuration) kernel.getGBean(gbeanData.getAbstractName());
153 
154         // start the gbeans
155         startConfigurationGBeans(configuration.getAbstractName(), configuration, kernel);
156 
157         ConfigurationManager configurationManager = getConfigurationManager(kernel);
158         configurationManager.loadConfiguration(configId);
159         return gbeanData.getAbstractName();
160     }
161 
162     public static void writeConfigurationData(ConfigurationData configurationData, OutputStream out) throws IOException {
163         configurationMarshaler.writeConfigurationData(configurationData, out);
164     }
165 
166     public static ConfigurationData readConfigurationData(InputStream in) throws IOException, ClassNotFoundException {
167         return configurationMarshaler.readConfigurationData(in);
168     }
169 
170     public static void writeConfigInfo(PrintWriter writer, ConfigurationData configurationData) {
171         writeConfigInfo("", writer, configurationData);
172     }
173     private static void writeConfigInfo(String prefix, PrintWriter writer, ConfigurationData configurationData) {
174         writer.println(prefix+"id=" + configurationData.getId());
175         writer.println(prefix+"type=" + configurationData.getModuleType());
176         writer.println(prefix+"created=" + configurationData.getCreated());
177         Set ownedConfigurations = configurationData.getOwnedConfigurations();
178         int i = 0;
179         for (Iterator iterator = ownedConfigurations.iterator(); iterator.hasNext();) {
180             Artifact ownedConfiguration = (Artifact) iterator.next();
181             writer.println(prefix+"owned." + i++ + "=" + ownedConfiguration);
182         }
183         i = 0;
184         for (Iterator it = configurationData.getChildConfigurations().values().iterator(); it.hasNext(); i++) {
185             ConfigurationData data = (ConfigurationData) it.next();
186             writeConfigInfo("child."+i+".", writer, data);
187         }
188         writer.flush();
189     }
190 
191     public static ConfigurationInfo readConfigurationInfo(InputStream in, AbstractName storeName, File inPlaceLocation) throws IOException {
192         Properties properties = new Properties();
193         properties.load(in);
194         return readConfigurationInfo("", properties, storeName, inPlaceLocation);
195     }
196     private static ConfigurationInfo readConfigurationInfo(String prefix, Properties properties, AbstractName storeName, File inPlaceLocation) throws IOException {
197         String id = properties.getProperty(prefix+"id");
198         Artifact configId = Artifact.create(id);
199 
200         String type = properties.getProperty(prefix+"type");
201         ConfigurationModuleType moduleType = ConfigurationModuleType.getByName(type);
202         if (moduleType == null) {
203             throw new IllegalArgumentException("Unknown module type: " + type);
204         }
205 
206         String created = properties.getProperty(prefix+"created");
207         long time;
208         try {
209             time = Long.parseLong(created);
210         } catch (NumberFormatException e) {
211             throw new IllegalArgumentException("Invalid created time: " + created);
212         }
213 
214         LinkedHashSet ownedConfigurations = new LinkedHashSet();
215         for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) {
216             Map.Entry entry = (Map.Entry) iterator.next();
217             String name = (String) entry.getKey();
218             if (name.startsWith(prefix+"owned.")) {
219                 String value = (String) entry.getValue();
220                 Artifact ownedConfiguration = Artifact.create(value);
221                 ownedConfigurations.add(ownedConfiguration);
222             }
223         }
224         LinkedHashSet childConfigurations = new LinkedHashSet();
225         int test = 0;
226         while(true) {
227             String next = prefix+"child."+test+".";
228             String value = properties.getProperty(next+".id");
229             if(value == null) {
230                 break;
231             }
232             childConfigurations.add(readConfigurationInfo(next, properties, storeName, inPlaceLocation));
233             ++test;
234         }
235 
236         return new ConfigurationInfo(storeName, configId, moduleType, time, ownedConfigurations, childConfigurations, inPlaceLocation);
237     }
238 
239     /**
240      * Gets a reference or proxy to the ConfigurationManager running in the specified kernel.
241      *
242      * @return The ConfigurationManager
243      * @throws IllegalStateException Occurs if a ConfigurationManager cannot be identified
244      */
245     public static ConfigurationManager getConfigurationManager(Kernel kernel) {
246         Set names = kernel.listGBeans(new AbstractNameQuery(ConfigurationManager.class.getName()));
247         for (Iterator iterator = names.iterator(); iterator.hasNext();) {
248             AbstractName abstractName = (AbstractName) iterator.next();
249             if (!kernel.isRunning(abstractName)) {
250                 iterator.remove();
251             }
252         }
253         if (names.isEmpty()) {
254             throw new IllegalStateException("A Configuration Manager could not be found in the kernel");
255         }
256         if (names.size() > 1) {
257             throw new IllegalStateException("More than one Configuration Manager was found in the kernel");
258         }
259         AbstractName configurationManagerName = (AbstractName) names.iterator().next();
260         return (ConfigurationManager) kernel.getProxyManager().createProxy(configurationManagerName, ConfigurationManager.class);
261     }
262 
263     /**
264      * Gets a reference or proxy to an EditableConfigurationManager running in the specified kernel, if there is one.
265      *
266      * @return The EdtiableConfigurationManager, or none if there is not one available.
267      * @throws IllegalStateException Occurs if there are multiple EditableConfigurationManagers in the kernel.
268      */
269     public static EditableConfigurationManager getEditableConfigurationManager(Kernel kernel) {
270         Set names = kernel.listGBeans(new AbstractNameQuery(EditableConfigurationManager.class.getName()));
271         for (Iterator iterator = names.iterator(); iterator.hasNext();) {
272             AbstractName abstractName = (AbstractName) iterator.next();
273             if (!kernel.isRunning(abstractName)) {
274                 iterator.remove();
275             }
276         }
277         if (names.isEmpty()) {
278             return null; // may be one, just not editable
279         }
280         if (names.size() > 1) {
281             throw new IllegalStateException("More than one Configuration Manager was found in the kernel");
282         }
283         AbstractName configurationManagerName = (AbstractName) names.iterator().next();
284         return (EditableConfigurationManager) kernel.getProxyManager().createProxy(configurationManagerName, EditableConfigurationManager.class);
285     }
286 
287     public static void releaseConfigurationManager(Kernel kernel, ConfigurationManager configurationManager) {
288         kernel.getProxyManager().destroyProxy(configurationManager);
289     }
290 
291     static void preprocessGBeanData(AbstractName configurationName, Configuration configuration, GBeanData gbeanData) throws InvalidConfigException {
292         for (Iterator references = gbeanData.getReferencesNames().iterator(); references.hasNext();) {
293             String referenceName = (String) references.next();
294             GReferenceInfo referenceInfo = gbeanData.getGBeanInfo().getReference(referenceName);
295             if (referenceInfo == null) {
296                 throw new InvalidConfigException("No reference named " + referenceName + " in gbean " + gbeanData.getAbstractName());
297             }
298             boolean isSingleValued = !referenceInfo.getProxyType().equals(Collection.class.getName());
299             if (isSingleValued) {
300                 ReferencePatterns referencePatterns = gbeanData.getReferencePatterns(referenceName);
301                 AbstractName abstractName;
302                 try {
303                     abstractName = configuration.findGBean(referencePatterns);
304                 } catch (GBeanNotFoundException e) {
305                     throw new InvalidConfigException("Unable to resolve reference \"" + referenceName + "\" in gbean " + gbeanData.getAbstractName() + " to a gbean matching the pattern " + referencePatterns, e);
306                 }
307                 gbeanData.setReferencePatterns(referenceName, new ReferencePatterns(abstractName));
308             }
309         }
310 
311         Set newDependencies = new HashSet();
312         for (Iterator dependencyIterator = gbeanData.getDependencies().iterator(); dependencyIterator.hasNext();) {
313             ReferencePatterns referencePatterns = (ReferencePatterns) dependencyIterator.next();
314             AbstractName abstractName;
315             try {
316                 abstractName = configuration.findGBean(referencePatterns);
317             } catch (GBeanNotFoundException e) {
318                 throw new InvalidConfigException("Unable to resolve dependency in gbean " + gbeanData.getAbstractName(), e);
319             }
320             newDependencies.add(new ReferencePatterns(abstractName));
321         }
322         gbeanData.setDependencies(newDependencies);
323 
324         // If the GBean has a configurationBaseUrl attribute, set it
325         // todo Even though this is not used by the classloader, web apps still need this.  WHY???
326         GAttributeInfo attribute = gbeanData.getGBeanInfo().getAttribute("configurationBaseUrl");
327         if (attribute != null && attribute.getType().equals("java.net.URL")) {
328             try {
329                 Set set = configuration.getConfigurationResolver().resolve("");
330                 if (set.size() != 1) {
331                     throw new AssertionError("Expected one match for pattern \".\", but got " + set.size() + " matches");
332                 }
333                 URL baseURL = (URL) set.iterator().next();
334                 gbeanData.setAttribute("configurationBaseUrl", baseURL);
335             } catch (Exception e) {
336                 throw new InvalidConfigException("Unable to set attribute named " + "configurationBaseUrl" + " in gbean " + gbeanData.getAbstractName(), e);
337             }
338         }
339 
340         // add a dependency from the gbean to the configuration
341         gbeanData.addDependency(configurationName);
342     }
343 
344     static void startConfigurationGBeans(AbstractName configurationName, Configuration configuration, Kernel kernel) throws InvalidConfigException {
345         List gbeans = new ArrayList(configuration.getGBeans().values());
346         Collections.sort(gbeans, new GBeanData.PriorityComparator());
347 
348         List loaded = new ArrayList(gbeans.size());
349         List started = new ArrayList(gbeans.size());
350 
351         try {
352             // register all the GBeans
353             for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) {
354                 GBeanData gbeanData = (GBeanData) iterator.next();
355 
356                 // copy the gbeanData object as not to mutate the original
357                 gbeanData = new GBeanData(gbeanData);
358 
359                 // preprocess the gbeanData (resolve references, set base url, declare dependency, etc.)
360                 preprocessGBeanData(configurationName, configuration, gbeanData);
361 
362                 try {
363                     kernel.loadGBean(gbeanData, configuration.getConfigurationClassLoader());
364                     loaded.add(gbeanData.getAbstractName());
365                 } catch (GBeanAlreadyExistsException e) {
366                     throw new InvalidConfigException(e);
367                 }
368             }
369 
370             try {
371                 // start the gbeans
372                 for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) {
373                     GBeanData gbeanData = (GBeanData) iterator.next();
374                     AbstractName gbeanName = gbeanData.getAbstractName();
375                     kernel.startRecursiveGBean(gbeanName);
376                     started.add(gbeanName);
377                 }
378 
379                 // assure all of the gbeans are started
380                 List unstarted = new ArrayList();
381                 for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) {
382                     GBeanData gbeanData = (GBeanData) iterator.next();
383                     AbstractName gbeanName = gbeanData.getAbstractName();
384                     if (State.RUNNING_INDEX != kernel.getGBeanState(gbeanName)) {
385                         String stateReason = null;
386                         if (kernel instanceof BasicKernel) {
387                             stateReason = ((BasicKernel) kernel).getStateReason(gbeanName);
388                         }
389                         String name = gbeanName.toURI().getQuery();
390                         if (stateReason != null) {
391                             unstarted.add("The service " + name + " did not start because " + stateReason);
392                         } else {
393                             unstarted.add("The service " + name + " did not start for an unknown reason");
394                         }
395                     }
396                 }
397                 if (!unstarted.isEmpty()) {
398                     StringBuffer message = new StringBuffer();
399                     message.append("Configuration ").append(configuration.getId()).append(" failed to start due to the following reasons:\n");
400                     for (Iterator iterator = unstarted.iterator(); iterator.hasNext();) {
401                         String reason = (String) iterator.next();
402                         message.append("  ").append(reason).append("\n");
403                     }
404                     throw new InvalidConfigurationException(message.toString());
405                 }
406             } catch (GBeanNotFoundException e) {
407                 throw new InvalidConfigException(e);
408             }
409 
410             for (Iterator iterator = configuration.getChildren().iterator(); iterator.hasNext();) {
411                 Configuration childConfiguration = (Configuration) iterator.next();
412                 ConfigurationUtil.startConfigurationGBeans(configurationName, childConfiguration, kernel);
413             }
414         } catch (Throwable e) {
415             for (Iterator iterator = started.iterator(); iterator.hasNext();) {
416                 AbstractName gbeanName = (AbstractName) iterator.next();
417                 try {
418                     kernel.stopGBean(gbeanName);
419                 } catch (GBeanNotFoundException ignored) {
420                 } catch (IllegalStateException ignored) {
421                 } catch (InternalKernelException kernelException) {
422                     log.debug("Error cleaning up after failed start of configuration " + configuration.getId() + " gbean " + gbeanName, kernelException);
423                 }
424             }
425             for (Iterator iterator = loaded.iterator(); iterator.hasNext();) {
426                 AbstractName gbeanName = (AbstractName) iterator.next();
427                 try {
428                     kernel.unloadGBean(gbeanName);
429                 } catch (GBeanNotFoundException ignored) {
430                 } catch (IllegalStateException ignored) {
431                 } catch (InternalKernelException kernelException) {
432                     log.debug("Error cleaning up after failed start of configuration " + configuration.getId() + " gbean " + gbeanName, kernelException);
433                 }
434             }
435             if (e instanceof Error) {
436                 throw (Error) e;
437             }                         
438             if (e instanceof InvalidConfigException) {
439                 throw (InvalidConfigException) e;
440             }
441             throw new InvalidConfigException("Unknown start exception", e);
442         }
443     }
444 }