001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.lib.conf;
020:
021: import java.io.File;
022: import java.security.AccessController;
023: import java.security.PrivilegedActionException;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.Comparator;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.MissingResourceException;
032:
033: import org.apache.commons.lang.StringUtils;
034: import org.apache.openjpa.lib.util.J2DoPrivHelper;
035: import org.apache.openjpa.lib.util.JavaVersions;
036: import org.apache.openjpa.lib.util.Localizer;
037: import org.apache.openjpa.lib.util.Services;
038:
039: /**
040: * Utilities for running product derivations.
041: *
042: * @author Abe White
043: * @nojavadoc
044: */
045: public class ProductDerivations {
046:
047: private static final Localizer _loc = Localizer
048: .forPackage(ProductDerivations.class);
049:
050: private static final ProductDerivation[] _derivations;
051: private static final String[] _derivationNames;
052: private static final Throwable[] _derivationErrors;
053: private static String[] _prefixes;
054: static {
055: ClassLoader l = (ClassLoader) AccessController
056: .doPrivileged(J2DoPrivHelper
057: .getClassLoaderAction(ProductDerivation.class));
058: _derivationNames = Services.getImplementors(
059: ProductDerivation.class, l);
060: _derivationErrors = new Throwable[_derivationNames.length];
061: List derivations = new ArrayList(_derivationNames.length);
062: for (int i = 0; i < _derivationNames.length; i++) {
063: try {
064: ProductDerivation d = (ProductDerivation) AccessController
065: .doPrivileged(J2DoPrivHelper
066: .newInstanceAction(Class.forName(
067: _derivationNames[i], true, l)));
068: d.validate();
069: derivations.add(d);
070: } catch (Throwable t) {
071: if (t instanceof PrivilegedActionException)
072: t = ((PrivilegedActionException) t).getException();
073: _derivationErrors[i] = t;
074: }
075: }
076:
077: // must be at least one product derivation to define metadata factories,
078: // etc.
079: if (derivations.isEmpty()) {
080: throw new MissingResourceException(_loc.get(
081: "no-product-derivations",
082: ProductDerivation.class.getName(),
083: derivationErrorsToString()).getMessage(),
084: ProductDerivations.class.getName(), "derivations");
085: }
086:
087: // if some derivations weren't instantiable, warn
088: for (int i = 0; i < _derivationErrors.length; i++) {
089: if (_derivationErrors[i] == null)
090: continue;
091: System.err.println(_loc.get("bad-product-derivations",
092: ProductDerivations.class.getName()));
093: break;
094: }
095:
096: Collections
097: .sort(derivations, new ProductDerivationComparator());
098: _derivations = (ProductDerivation[]) derivations
099: .toArray(new ProductDerivation[derivations.size()]);
100:
101: List prefixes = new ArrayList(2);
102: for (int i = 0; i < _derivations.length; i++) {
103: if (_derivations[i].getConfigurationPrefix() != null
104: && !"openjpa".equals(_derivations[i]
105: .getConfigurationPrefix()))
106: prefixes.add(_derivations[i].getConfigurationPrefix());
107: }
108: String[] prefixArray = new String[1 + prefixes.size()];
109: prefixArray[0] = "openjpa";
110: for (int i = 0; i < prefixes.size(); i++)
111: prefixArray[i + 1] = (String) prefixes.get(i);
112: setConfigurationPrefixes(prefixArray);
113: }
114:
115: /**
116: * Return all the product derivations registered in the current classloader
117: */
118: public static ProductDerivation[] getProductDerivations() {
119: return _derivations;
120: }
121:
122: /**
123: * Return the recognized prefixes for configuration properties.
124: */
125: public static String[] getConfigurationPrefixes() {
126: return _prefixes;
127: }
128:
129: /**
130: * Set the configuration prefix array. This is package-visible for
131: * testing purposes.
132: *
133: * @since 0.9.7
134: */
135: static void setConfigurationPrefixes(String[] prefixes) {
136: _prefixes = prefixes;
137: }
138:
139: /**
140: * Determine the full key name for <code>key</code>, given the registered
141: * prefixes and the entries in <code>map</code>. This method
142: * computes the appropriate configuration prefix to use by looking
143: * through <code>map</code> for a key starting with any of the known
144: * configuration prefixes and ending with <code>key</code> and, if a
145: * value is found, using the prefix of that key. Otherwise, it uses
146: * the first registered prefix.
147: *
148: * @since 0.9.7
149: */
150: public static String getConfigurationKey(String partialKey, Map map) {
151: String firstKey = null;
152: for (int i = 0; map != null && i < _prefixes.length; i++) {
153: String fullKey = _prefixes[i] + "." + partialKey;
154: if (map.containsKey(fullKey)) {
155: if (firstKey == null)
156: firstKey = fullKey;
157: else {
158: // if we've already found a property with a previous
159: // prefix, then this is a collision.
160: throw new IllegalStateException(_loc.get(
161: "dup-with-different-prefixes", firstKey,
162: fullKey).getMessage());
163: }
164: }
165: }
166:
167: if (firstKey == null)
168: return _prefixes[0] + "." + partialKey;
169: else
170: return firstKey;
171: }
172:
173: /**
174: * Apply {@link ProductDerivation#beforeConfigurationConstruct} callbacks
175: * to the the given instance. Exceptions other than fatal
176: * {@link BootstrapException} are swallowed.
177: */
178: public static void beforeConfigurationConstruct(
179: ConfigurationProvider cp) {
180: for (int i = 0; i < _derivations.length; i++) {
181: try {
182: _derivations[i].beforeConfigurationConstruct(cp);
183: } catch (BootstrapException be) {
184: if (be.isFatal())
185: throw be;
186: } catch (Exception e) {
187: // can't log; no configuration yet
188: e.printStackTrace();
189: }
190: }
191: }
192:
193: /**
194: * Apply {@link ProductDerivation#beforeConfigurationLoad} callbacks
195: * to the the given instance. Exceptions other than fatal
196: * {@link BootstrapException} are swallowed.
197: */
198: public static void beforeConfigurationLoad(Configuration conf) {
199: for (int i = 0; i < _derivations.length; i++) {
200: try {
201: _derivations[i].beforeConfigurationLoad(conf);
202: } catch (BootstrapException be) {
203: if (be.isFatal())
204: throw be;
205: } catch (Exception e) {
206: // logging not configured yet
207: e.printStackTrace();
208: }
209: }
210: }
211:
212: /**
213: * Apply {@link ProductDerivation#afterSpecificationSet} callbacks
214: * to the the given instance. Exceptions other than fatal
215: * {@link BootstrapException} are swallowed.
216: */
217: public static void afterSpecificationSet(Configuration conf) {
218: for (int i = 0; i < _derivations.length; i++) {
219: try {
220: _derivations[i].afterSpecificationSet(conf);
221: } catch (BootstrapException be) {
222: if (be.isFatal())
223: throw be;
224: } catch (Exception e) {
225: // logging not configured yet
226: e.printStackTrace();
227: }
228: }
229: }
230:
231: /**
232: * Called as the first step of a Configuration's close() method.
233: * Exceptions are swallowed.
234: *
235: * @since 0.9.7
236: */
237: public static void beforeClose(Configuration conf) {
238: for (int i = 0; i < _derivations.length; i++) {
239: try {
240: _derivations[i].beforeConfigurationClose(conf);
241: } catch (Exception e) {
242: conf.getConfigurationLog().warn(
243: _loc.get("before-close-ex"), e);
244: }
245: }
246: }
247:
248: /**
249: * Load the given given resource, or return false if it is not a resource
250: * this provider understands. The given class loader may be null.
251: *
252: * @param anchor optional named anchor within a multiple-configuration
253: * resource
254: */
255: public static ConfigurationProvider load(String resource,
256: String anchor, ClassLoader loader) {
257: if (StringUtils.isEmpty(resource))
258: return null;
259: if (loader == null)
260: loader = (ClassLoader) AccessController
261: .doPrivileged(J2DoPrivHelper
262: .getContextClassLoaderAction());
263: ConfigurationProvider provider = null;
264: StringBuffer errs = null;
265: // most specific to least
266: Throwable err = null;
267: for (int i = _derivations.length - 1; i >= 0; i--) {
268: try {
269: provider = _derivations[i].load(resource, anchor,
270: loader);
271: if (provider != null)
272: return provider;
273: } catch (Throwable t) {
274: err = t;
275: errs = (errs == null) ? new StringBuffer() : errs
276: .append("\n");
277: errs.append(_derivations[i].getClass().getName() + ":"
278: + t);
279: }
280: }
281: reportErrors(errs, resource, err);
282: throw (MissingResourceException) JavaVersions.initCause(
283: new MissingResourceException(resource,
284: ProductDerivations.class.getName(), resource),
285: err);
286: }
287:
288: /**
289: * Load given file, or return false if it is not a file this provider
290: * understands.
291: *
292: * @param anchor optional named anchor within a multiple-configuration file
293: */
294: public static ConfigurationProvider load(File file, String anchor,
295: ClassLoader loader) {
296: if (file == null)
297: return null;
298: if (loader == null)
299: loader = (ClassLoader) AccessController
300: .doPrivileged(J2DoPrivHelper
301: .getContextClassLoaderAction());
302: ConfigurationProvider provider = null;
303: StringBuffer errs = null;
304: Throwable err = null;
305: // most specific to least
306: for (int i = _derivations.length - 1; i >= 0; i--) {
307: try {
308: provider = _derivations[i].load(file, anchor);
309: if (provider != null)
310: return provider;
311: } catch (Throwable t) {
312: err = t;
313: errs = (errs == null) ? new StringBuffer() : errs
314: .append("\n");
315: errs.append(_derivations[i].getClass().getName() + ":"
316: + t);
317: }
318: }
319: String aPath = (String) AccessController
320: .doPrivileged(J2DoPrivHelper
321: .getAbsolutePathAction(file));
322: reportErrors(errs, aPath, err);
323: throw (MissingResourceException) JavaVersions
324: .initCause(new MissingResourceException(aPath,
325: ProductDerivations.class.getName(), aPath), err);
326: }
327:
328: /**
329: * Return a {@link ConfigurationProvider} that has parsed system defaults.
330: */
331: public static ConfigurationProvider loadDefaults(ClassLoader loader) {
332: return load(loader, false);
333: }
334:
335: /**
336: * Return a {@link ConfigurationProvider} that has parsed system globals.
337: */
338: public static ConfigurationProvider loadGlobals(ClassLoader loader) {
339: return load(loader, true);
340: }
341:
342: /**
343: * Load a built-in resource location.
344: */
345: private static ConfigurationProvider load(ClassLoader loader,
346: boolean globals) {
347: if (loader == null)
348: loader = (ClassLoader) AccessController
349: .doPrivileged(J2DoPrivHelper
350: .getContextClassLoaderAction());
351:
352: ConfigurationProvider provider = null;
353: StringBuffer errs = null;
354: String type = (globals) ? "globals" : "defaults";
355: Throwable err = null;
356: // most specific to least
357: for (int i = _derivations.length - 1; i >= 0; i--) {
358: try {
359: provider = (globals) ? _derivations[i]
360: .loadGlobals(loader) : _derivations[i]
361: .loadDefaults(loader);
362: if (provider != null)
363: return provider;
364: } catch (Throwable t) {
365: err = t;
366: errs = (errs == null) ? new StringBuffer() : errs
367: .append("\n");
368: errs.append(_derivations[i].getClass().getName() + ":"
369: + t);
370: }
371: }
372: reportErrors(errs, type, err);
373: return null;
374: }
375:
376: /**
377: * Thrown proper exception for given errors.
378: */
379: private static void reportErrors(StringBuffer errs,
380: String resource, Throwable nested) {
381: if (errs == null)
382: return;
383: throw (MissingResourceException) JavaVersions.initCause(
384: new MissingResourceException(errs.toString(),
385: ProductDerivations.class.getName(), resource),
386: nested);
387: }
388:
389: /**
390: * Return a List<String> of all the fully-qualified anchors specified in
391: * <code>propertiesLocation</code>. The return values must be used in
392: * conjunction with <code>propertiesLocation</code>. If there are no
393: * product derivations or if no product derivations could find anchors,
394: * this returns an empty list.
395: *
396: * @since 1.1.0
397: */
398: public static List getFullyQualifiedAnchorsInPropertiesLocation(
399: final String propertiesLocation) {
400: List fqAnchors = new ArrayList();
401: StringBuffer errs = null;
402: Throwable err = null;
403: for (int i = _derivations.length - 1; i >= 0; i--) {
404: try {
405: if (propertiesLocation == null) {
406: String loc = _derivations[i]
407: .getDefaultResourceLocation();
408: addAll(fqAnchors, loc, _derivations[i]
409: .getAnchorsInResource(loc));
410: continue;
411: }
412:
413: File f = new File(propertiesLocation);
414: if (((Boolean) J2DoPrivHelper.isFileAction(f).run())
415: .booleanValue()) {
416: addAll(fqAnchors, propertiesLocation,
417: _derivations[i].getAnchorsInFile(f));
418: } else {
419: f = new File("META-INF" + File.separatorChar
420: + propertiesLocation);
421: if (((Boolean) J2DoPrivHelper.isFileAction(f).run())
422: .booleanValue()) {
423: addAll(fqAnchors, propertiesLocation,
424: _derivations[i].getAnchorsInFile(f));
425: } else {
426: addAll(
427: fqAnchors,
428: propertiesLocation,
429: _derivations[i]
430: .getAnchorsInResource(propertiesLocation));
431: }
432: }
433: } catch (Throwable t) {
434: err = t;
435: errs = (errs == null) ? new StringBuffer() : errs
436: .append("\n");
437: errs.append(_derivations[i].getClass().getName() + ":"
438: + t);
439: }
440: }
441: reportErrors(errs, propertiesLocation, err);
442: return fqAnchors;
443: }
444:
445: private static void addAll(Collection collection, String base,
446: Collection newMembers) {
447: if (newMembers == null || collection == null)
448: return;
449: for (Iterator iter = newMembers.iterator(); iter.hasNext();) {
450: String fqLoc = base + "#" + iter.next();
451: if (!collection.contains(fqLoc))
452: collection.add(fqLoc);
453: }
454: }
455:
456: /**
457: * Compare {@link ProductDerivation}s.
458: */
459: private static class ProductDerivationComparator implements
460: Comparator {
461:
462: public int compare(Object o1, Object o2) {
463: int type1 = ((ProductDerivation) o1).getType();
464: int type2 = ((ProductDerivation) o2).getType();
465: if (type1 != type2)
466: return type1 - type2;
467:
468: // arbitrary but consistent order
469: return o1.getClass().getName().compareTo(
470: o2.getClass().getName());
471: }
472: }
473:
474: /**
475: * Prints product derivation information.
476: */
477: public static void main(String[] args) {
478: System.err.println(derivationErrorsToString());
479: }
480:
481: /**
482: * Return a message about the status of each product derivation.
483: */
484: private static String derivationErrorsToString() {
485: StringBuffer buf = new StringBuffer();
486: buf.append("ProductDerivations: ").append(
487: _derivationNames.length);
488: for (int i = 0; i < _derivationNames.length; i++) {
489: buf.append("\n").append(i + 1).append(". ").append(
490: _derivationNames[i]).append(": ");
491: if (_derivationErrors[i] == null)
492: buf.append("OK");
493: else
494: buf.append(_derivationErrors[i].toString());
495: }
496: return buf.toString();
497: }
498: }
|