| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.lib.conf; |
| |
| import java.io.File; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.MissingResourceException; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.MultiClassLoader; |
| import org.apache.openjpa.lib.util.Services; |
| import org.apache.openjpa.lib.util.StringUtil; |
| |
| /** |
| * Utilities for running product derivations. |
| * |
| * @author Abe White |
| * @author Pinaki Poddar |
| */ |
| public class ProductDerivations { |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (ProductDerivations.class); |
| |
| private static final ProductDerivation[] _derivations; |
| private static final String[] _derivationNames; |
| private static final Throwable[] _derivationErrors; |
| private static String[] _prefixes; |
| static { |
| MultiClassLoader l = AccessController.doPrivileged(J2DoPrivHelper.newMultiClassLoaderAction()); |
| l.addClassLoader(0, AccessController |
| .doPrivileged(J2DoPrivHelper.getClassLoaderAction(ProductDerivations.class))); |
| l.addClassLoader(1, AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction())); |
| _derivationNames = Services.getImplementors(ProductDerivation.class, l); |
| _derivationErrors = new Throwable[_derivationNames.length]; |
| List<ProductDerivation> derivations = |
| new ArrayList<>(_derivationNames.length); |
| boolean errors = false; |
| for (int i = 0; i < _derivationNames.length; i++) { |
| try { |
| ProductDerivation d = (ProductDerivation) |
| AccessController.doPrivileged( |
| J2DoPrivHelper.newInstanceAction( |
| Class.forName(_derivationNames[i], true, l))); |
| d.validate(); |
| derivations.add(d); |
| } catch (Throwable t) { |
| if (t instanceof PrivilegedActionException) |
| t = ((PrivilegedActionException) t).getException(); |
| _derivationErrors[i] = t; |
| errors = true; |
| } |
| } |
| |
| // must be at least one product derivation to define metadata factories, |
| // etc. |
| if (derivations.isEmpty()) { |
| throw new MissingResourceException(_loc.get |
| ("no-product-derivations", ProductDerivation.class.getName(), |
| derivationErrorsToString()).getMessage(), |
| ProductDerivations.class.getName(),"derivations"); |
| } |
| |
| // START - ALLOW PRINT STATEMENTS |
| // if some derivations weren't instantiable, warn |
| if (errors) |
| System.err.println(_loc.get("bad-product-derivations", |
| ProductDerivations.class.getName())); |
| for (int i = 0; i < _derivationErrors.length; i++) { |
| if (_derivationErrors[i] == null) |
| continue; |
| System.err.println(_derivationNames[i] + ":" + |
| _derivationErrors[i]); |
| break; |
| } |
| // STOP - ALLOW PRINT STATEMENTS |
| |
| Collections.sort(derivations, new ProductDerivationComparator()); |
| _derivations = |
| derivations.toArray(new ProductDerivation[derivations.size()]); |
| |
| List<String> prefixes = new ArrayList<>(2); |
| prefixes.add("openjpa"); |
| for (ProductDerivation derivation : _derivations) { |
| String prefix = derivation.getConfigurationPrefix(); |
| if (prefix != null && !"openjpa".equals(prefix)) |
| prefixes.add(prefix); |
| } |
| _prefixes = prefixes.toArray(new String[prefixes.size()]); |
| } |
| |
| /** |
| * Return all the product derivations registered in the current classloader |
| */ |
| public static ProductDerivation[] getProductDerivations() { |
| return _derivations; |
| } |
| |
| /** |
| * Return the recognized prefixes for configuration properties. |
| */ |
| public static String[] getConfigurationPrefixes() { |
| return _prefixes; |
| } |
| |
| /** |
| * Set the configuration prefix array. This is package-visible for |
| * testing purposes. |
| * |
| * @since 0.9.7 |
| */ |
| static void setConfigurationPrefixes(String[] prefixes) { |
| _prefixes = prefixes; |
| } |
| |
| /** |
| * Determine the full key name for <code>partialKey</code>, given the |
| * registered prefixes and the entries in <code>map</code>. This method |
| * computes the appropriate configuration prefix to use by looking |
| * through <code>map</code> for a key starting with any of the known |
| * configuration prefixes and ending with <code>partialKey</code> and, if a |
| * value is found, using the prefix of that key. Otherwise, it uses |
| * the first registered prefix. |
| * |
| * The given <code>partialKey</code> is first tested for containment in the |
| * given map without any prefix. |
| * |
| * @since 0.9.7 |
| */ |
| public static String getConfigurationKey(String partialKey, Map map) { |
| String firstKey = (map != null && map.containsKey(partialKey)) |
| ? partialKey : null; |
| for (int i = 0; map != null && i < _prefixes.length; i++) { |
| String fullKey = _prefixes[i] + "." + partialKey; |
| if (map.containsKey(fullKey)) { |
| if (firstKey == null) |
| firstKey = fullKey; |
| else { |
| // if we've already found a property with a previous |
| // prefix, then this is a collision. |
| throw new IllegalStateException(_loc.get( |
| "dup-with-different-prefixes", firstKey, fullKey) |
| .getMessage()); |
| } |
| } |
| } |
| |
| if (firstKey == null) |
| return _prefixes[0] + "." + partialKey; |
| else |
| return firstKey; |
| } |
| |
| /** |
| * Apply {@link ProductDerivation#beforeConfigurationConstruct} callbacks |
| * to the the given instance. Exceptions other than fatal |
| * {@link BootstrapException} are swallowed. |
| */ |
| public static void beforeConfigurationConstruct(ConfigurationProvider cp) { |
| for (ProductDerivation derivation : _derivations) { |
| try { |
| derivation.beforeConfigurationConstruct(cp); |
| } |
| catch (BootstrapException be) { |
| if (be.isFatal()) |
| throw be; |
| } |
| catch (Exception e) { |
| // can't log; no configuration yet |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| /** |
| * Apply {@link ProductDerivation#beforeConfigurationLoad} callbacks |
| * to the the given instance. Exceptions other than fatal |
| * {@link BootstrapException} are swallowed. |
| */ |
| public static void beforeConfigurationLoad(Configuration conf) { |
| for (ProductDerivation derivation : _derivations) { |
| try { |
| derivation.beforeConfigurationLoad(conf); |
| } |
| catch (BootstrapException be) { |
| if (be.isFatal()) |
| throw be; |
| } |
| catch (Exception e) { |
| // logging not configured yet |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| /** |
| * Apply {@link ProductDerivation#afterSpecificationSet} callbacks |
| * to the the given instance. Exceptions other than fatal |
| * {@link BootstrapException} are swallowed. |
| */ |
| public static void afterSpecificationSet(Configuration conf) { |
| for (ProductDerivation derivation : _derivations) { |
| try { |
| derivation.afterSpecificationSet(conf); |
| } |
| catch (BootstrapException be) { |
| if (be.isFatal()) |
| throw be; |
| } |
| catch (Exception e) { |
| // logging not configured yet |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| /** |
| * Called as the first step of a Configuration's close() method. |
| * Exceptions are swallowed. |
| * |
| * @since 0.9.7 |
| */ |
| public static void beforeClose(Configuration conf) { |
| for (ProductDerivation derivation : _derivations) { |
| try { |
| derivation.beforeConfigurationClose(conf); |
| } |
| catch (Exception e) { |
| conf.getConfigurationLog().warn(_loc.get("before-close-ex"), e); |
| } |
| } |
| } |
| |
| /** |
| * Load the given given resource, or return false if it is not a resource |
| * this provider understands. The given class loader may be null. |
| * |
| * @param anchor optional named anchor within a multiple-configuration |
| * resource |
| */ |
| public static ConfigurationProvider load(String resource, String anchor, |
| ClassLoader loader) { |
| if (StringUtil.isEmpty(resource)) |
| return null; |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| ConfigurationProvider provider = null; |
| StringBuilder errs = null; |
| // most specific to least |
| Throwable err = null; |
| for (int i = _derivations.length - 1; i >= 0; i--) { |
| try { |
| provider = _derivations[i].load(resource, anchor, loader); |
| if (provider != null) |
| return provider; |
| } catch (Throwable t) { |
| err = t; |
| errs = (errs == null) ? new StringBuilder() : errs.append("\n"); |
| errs.append(_derivations[i].getClass().getName() + ":" + t); |
| } |
| } |
| reportErrors(errs, resource, err); |
| String rsrc = resource + "#" + anchor; |
| MissingResourceException ex = new MissingResourceException(rsrc, |
| ProductDerivations.class.getName(), rsrc); |
| ex.initCause(err); |
| throw ex; |
| } |
| |
| /** |
| * Load given file, or return false if it is not a file this provider |
| * understands. |
| * |
| * @param anchor optional named anchor within a multiple-configuration file |
| */ |
| public static ConfigurationProvider load(File file, String anchor, |
| ClassLoader loader) { |
| if (file == null) |
| return null; |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| ConfigurationProvider provider = null; |
| StringBuilder errs = null; |
| Throwable err = null; |
| // most specific to least |
| for (int i = _derivations.length - 1; i >= 0; i--) { |
| try { |
| provider = _derivations[i].load(file, anchor); |
| if (provider != null) |
| return provider; |
| } catch (Throwable t) { |
| err = t; |
| errs = (errs == null) ? new StringBuilder() : errs.append("\n"); |
| errs.append(_derivations[i].getClass().getName() + ":" + t); |
| } |
| } |
| String aPath = AccessController.doPrivileged( |
| J2DoPrivHelper.getAbsolutePathAction(file)); |
| reportErrors(errs, aPath, err); |
| String rsrc = aPath + "#" + anchor; |
| MissingResourceException ex = new MissingResourceException(rsrc, |
| ProductDerivations.class.getName(), rsrc); |
| ex.initCause(err); |
| throw ex; |
| } |
| |
| /** |
| * Return a {@link ConfigurationProvider} that has parsed system defaults. |
| */ |
| public static ConfigurationProvider loadDefaults(ClassLoader loader) { |
| return load(loader, false); |
| } |
| |
| /** |
| * Return a {@link ConfigurationProvider} that has parsed system globals. |
| */ |
| public static ConfigurationProvider loadGlobals(ClassLoader loader) { |
| return load(loader, true); |
| } |
| |
| /** |
| * Load a built-in resource location. |
| */ |
| private static ConfigurationProvider load(ClassLoader loader, |
| boolean globals) { |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| |
| ConfigurationProvider provider = null; |
| StringBuilder errs = null; |
| String type = (globals) ? "globals" : "defaults"; |
| Throwable err = null; |
| // most specific to least |
| for (int i = _derivations.length - 1; i >= 0; i--) { |
| try { |
| provider = (globals) ? _derivations[i].loadGlobals(loader) |
| : _derivations[i].loadDefaults(loader); |
| if (provider != null) |
| return provider; |
| } catch (Throwable t) { |
| err = t; |
| errs = (errs == null) ? new StringBuilder() : errs.append("\n"); |
| errs.append(_derivations[i].getClass().getName() + ":" + t); |
| } |
| } |
| reportErrors(errs, type, err); |
| return null; |
| } |
| |
| /** |
| * Thrown proper exception for given errors. |
| */ |
| private static void reportErrors(StringBuilder errs, String resource, |
| Throwable nested) { |
| if (errs == null) |
| return; |
| MissingResourceException ex = new MissingResourceException(errs.toString(), |
| ProductDerivations.class.getName(), resource); |
| ex.initCause(nested); |
| throw ex; |
| } |
| |
| /** |
| * Return a List<String> of all the fully-qualified anchors specified in |
| * <code>propertiesLocation</code>. The return values must be used in |
| * conjunction with <code>propertiesLocation</code>. If there are no |
| * product derivations or if no product derivations could find anchors, |
| * this returns an empty list. |
| * |
| * @since 1.1.0 |
| */ |
| public static List<String> getFullyQualifiedAnchorsInPropertiesLocation( |
| final String propertiesLocation) { |
| List<String> fqAnchors = new ArrayList<>(); |
| StringBuilder errs = null; |
| Throwable err = null; |
| for (int i = _derivations.length - 1; i >= 0; i--) { |
| try { |
| if (propertiesLocation == null) { |
| String loc = _derivations[i].getDefaultResourceLocation(); |
| addAll(fqAnchors, loc, |
| _derivations[i].getAnchorsInResource(loc)); |
| continue; |
| } |
| |
| File f = new File(propertiesLocation); |
| if ((Boolean) J2DoPrivHelper.isFileAction(f).run()) { |
| addAll(fqAnchors, propertiesLocation, |
| _derivations[i].getAnchorsInFile(f)); |
| } else { |
| f = new File("META-INF" + File.separatorChar |
| + propertiesLocation); |
| if ((Boolean) J2DoPrivHelper.isFileAction(f).run()) { |
| addAll(fqAnchors, propertiesLocation, |
| _derivations[i].getAnchorsInFile(f)); |
| } else { |
| addAll(fqAnchors, propertiesLocation, |
| _derivations[i].getAnchorsInResource( |
| propertiesLocation)); |
| } |
| } |
| } catch (Throwable t) { |
| err = t; |
| errs = (errs == null) ? new StringBuilder() : errs.append("\n"); |
| errs.append(_derivations[i].getClass().getName() + ":" + t); |
| } |
| } |
| reportErrors(errs, propertiesLocation, err); |
| return fqAnchors; |
| } |
| |
| private static void addAll(Collection collection, String base, |
| Collection newMembers) { |
| if (newMembers == null || collection == null) |
| return; |
| for (Object newMember : newMembers) { |
| String fqLoc = base + "#" + newMember; |
| if (!collection.contains(fqLoc)) |
| collection.add(fqLoc); |
| } |
| } |
| |
| |
| public static Set<String> getSupportedQueryHints() { |
| Set<String> result = new TreeSet<>(); |
| // most specific to least |
| for (int i = _derivations.length - 1; i >= 0; i--) { |
| Set<String> members = _derivations[i].getSupportedQueryHints(); |
| if (members != null && !members.isEmpty()) |
| result.addAll(members); |
| } |
| return result; |
| } |
| |
| |
| /** |
| * Compare {@link ProductDerivation}s. |
| */ |
| private static class ProductDerivationComparator |
| implements Comparator<ProductDerivation> { |
| |
| @Override |
| public int compare(ProductDerivation o1, ProductDerivation o2) { |
| int type1 = o1.getType(); |
| int type2 = o2.getType(); |
| if (type1 != type2) |
| return type1 - type2; |
| |
| // arbitrary but consistent order |
| return o1.getClass().getName().compareTo(o2.getClass(). |
| getName()); |
| } |
| } |
| |
| /** |
| * Prints product derivation information. |
| */ |
| public static void main(String[] args) { |
| // START - ALLOW PRINT STATEMENTS |
| System.err.println(derivationErrorsToString()); |
| // STOP - ALLOW PRINT STATEMENTS |
| } |
| |
| /** |
| * Return a message about the status of each product derivation. |
| */ |
| private static String derivationErrorsToString() { |
| StringBuilder buf = new StringBuilder(); |
| buf.append("ProductDerivations: ").append(_derivationNames.length); |
| for (int i = 0; i < _derivationNames.length; i++) { |
| buf.append("\n").append(i + 1).append(". "). |
| append(_derivationNames[i]).append(": "); |
| if (_derivationErrors[i] == null) |
| buf.append("OK"); |
| else |
| buf.append(_derivationErrors[i].toString()); |
| } |
| return buf.toString(); |
| } |
| } |
| |