001 /**
002 *
003 * Copyright 2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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.IOException;
020 import java.io.InputStream;
021 import java.io.OutputStream;
022 import java.io.PrintWriter;
023 import java.io.File;
024 import java.net.URL;
025 import java.util.Collection;
026 import java.util.HashSet;
027 import java.util.Iterator;
028 import java.util.Set;
029 import java.util.List;
030 import java.util.Collections;
031 import java.util.ArrayList;
032 import java.util.Properties;
033 import java.util.Map;
034 import java.util.LinkedHashSet;
035
036 import org.apache.geronimo.gbean.AbstractName;
037 import org.apache.geronimo.gbean.AbstractNameQuery;
038 import org.apache.geronimo.gbean.GAttributeInfo;
039 import org.apache.geronimo.gbean.GBeanData;
040 import org.apache.geronimo.gbean.GReferenceInfo;
041 import org.apache.geronimo.gbean.InvalidConfigurationException;
042 import org.apache.geronimo.gbean.ReferencePatterns;
043 import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
044 import org.apache.geronimo.kernel.GBeanNotFoundException;
045 import org.apache.geronimo.kernel.Kernel;
046 import org.apache.geronimo.kernel.ClassLoading;
047 import org.apache.geronimo.kernel.InternalKernelException;
048 import org.apache.geronimo.kernel.basic.BasicKernel;
049 import org.apache.geronimo.kernel.management.State;
050 import org.apache.geronimo.kernel.repository.Artifact;
051 import org.apache.commons.logging.Log;
052 import org.apache.commons.logging.LogFactory;
053
054 /**
055 * @version $Rev:386276 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $
056 */
057 public final class ConfigurationUtil {
058 private static final Log log = LogFactory.getLog(ConfigurationUtil.class);
059 private static final ConfigurationMarshaler configurationMarshaler;
060
061 static {
062 ConfigurationMarshaler marshaler = null;
063 String marshalerClass = System.getProperty("Xorg.apache.geronimo.kernel.config.Marshaler");
064 if (marshalerClass != null) {
065 try {
066 marshaler = createConfigurationMarshaler(marshalerClass);
067 } catch (Exception e) {
068 log.error("Error creating configuration marshaler class " + marshalerClass , e);
069 }
070 }
071
072 // todo this code effectively makes the default format xstream
073 //if (marshaler == null) {
074 // try {
075 // marshaler = createConfigurationMarshaler("org.apache.geronimo.kernel.config.xstream.XStreamConfigurationMarshaler");
076 // } catch (Throwable ignored) {
077 // }
078 //}
079
080 if (marshaler == null) {
081 marshaler = new SerializedConfigurationMarshaler();
082 }
083
084 configurationMarshaler = marshaler;
085 }
086
087 public static ConfigurationMarshaler createConfigurationMarshaler(String marshalerClass) throws Exception {
088 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
089 Class clazz = null;
090 if (classLoader != null) {
091 try {
092 clazz = ClassLoading.loadClass(marshalerClass, classLoader);
093 } catch (ClassNotFoundException ignored) {
094 // doesn't matter
095 }
096 }
097 if (clazz == null) {
098 classLoader = ConfigurationUtil.class.getClassLoader();
099 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 }