All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.hk2.utilities.BuilderHelper Maven / Gradle / Ivy

There is a newer version: 4.0.0-M3
Show newest version
/*
 * Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.hk2.utilities;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.Descriptor;
import org.glassfish.hk2.api.DescriptorType;
import org.glassfish.hk2.api.DescriptorVisibility;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.Filter;
import org.glassfish.hk2.api.IndexedFilter;
import org.glassfish.hk2.api.Metadata;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ProxyForSameScope;
import org.glassfish.hk2.api.Rank;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.UseProxy;
import org.glassfish.hk2.api.Visibility;
import org.glassfish.hk2.internal.ActiveDescriptorBuilderImpl;
import org.glassfish.hk2.internal.ConstantActiveDescriptor;
import org.glassfish.hk2.internal.DescriptorBuilderImpl;
import org.glassfish.hk2.internal.IndexedFilterImpl;
import org.glassfish.hk2.internal.SpecificFilterImpl;
import org.glassfish.hk2.internal.StarFilter;
import org.glassfish.hk2.utilities.reflection.ReflectionHelper;
import org.jvnet.hk2.annotations.Contract;
import org.jvnet.hk2.annotations.ContractsProvided;
import org.jvnet.hk2.annotations.Service;

/**
 * This class is used to generate DescriptorBuilders to be used
 * as a simple mechanism to create a Filter or Descriptor.
 */
public class BuilderHelper {
    /**
     * Returns an indexed filter that will return all descriptors that
     * have contract as an advertised contract
     * 
     * @param contract The advertised contract to look for
     * @return The indexed filter that can be used to calls to ServiceLocator methods
     */
    public static IndexedFilter createContractFilter(String contract) {
        return new IndexedFilterImpl(contract, null);
    }
    
    /**
     * Returns an indexed filter that will return all descriptors that
     * have the given name
     * 
     * @param name The name to look for
     * @return The indexed filter that can be used to calls to ServiceLocator methods
     */
    public static IndexedFilter createNameFilter(String name) {
        return new IndexedFilterImpl(null, name);
    }
    
    /**
     * Returns an indexed filter that will return all descriptors that
     * have the given name and given contract
     * 
     * @param contract The advertised contract to look for
     * @param name The name to look for
     * @return The indexed filter that can be used to calls to ServiceLocator methods
     */
    public static IndexedFilter createNameAndContractFilter(String contract, String name) {
        return new IndexedFilterImpl(contract, name);
    }
    
    /** The key for the name field of the tokenized string */
    public final static String NAME_KEY = "name";
    
    /** The key for the qualifier field of the tokenized string */
    public final static String QUALIFIER_KEY = "qualifier";
    
    /** The token separator */
    public final static String TOKEN_SEPARATOR = ";";
    
    /**
     * Creates a filter from a token string as per the following rules.
    *
  1. The token delimiter is ;
  2. *
  3. The first token is the contract. If the tokenString starts with ; there is no contract
  4. *
  5. All other tokens are in key = value form (with = being the separator)
  6. *
  7. A valid key that can appear only once is "name"
  8. *
  9. A valid key that can appear any number of times is "qualifier"
  10. *
*

* The following are examples of valid token strings:

    *
  • com.acme.product.RocketShoes
  • *
  • com.acme.product.Sneakers;name=Nike
  • *
  • com.acme.product.Sneakers;name=Nike;qualifier=com.acme.color.Red;qualifier=com.acme.style.HighTop
  • *
  • ;name=Narcissus
  • *
* * @param tokenString A non-null string following the rules stated above * @return A filter that will match the given string * @throws IllegalArgumentException If the token string is invalid in some way */ public static IndexedFilter createTokenizedFilter(String tokenString) throws IllegalArgumentException { if (tokenString == null) throw new IllegalArgumentException("null passed to createTokenizedFilter"); StringTokenizer st = new StringTokenizer(tokenString, TOKEN_SEPARATOR); String contract = null; String name = null; final Set qualifiers = new LinkedHashSet(); boolean firstToken = true; if (tokenString.startsWith(TOKEN_SEPARATOR)) { firstToken = false; } while (st.hasMoreTokens()) { String token = st.nextToken(); if (firstToken) { firstToken = false; if (token.length() <= 0) continue; contract = token; } else { int index = token.indexOf('='); if (index < 0) { throw new IllegalArgumentException("No = character found in token " + token); } String leftHandSide = token.substring(0, index); String rightHandSide = token.substring(index + 1); if (rightHandSide.length() <= 0) { throw new IllegalArgumentException("No value found in token " + token); } if (NAME_KEY.equals(leftHandSide)) { name = rightHandSide; } else if (QUALIFIER_KEY.equals(leftHandSide)) { qualifiers.add(rightHandSide); } else { throw new IllegalArgumentException("Unknown key: " + leftHandSide); } } } final String fContract = contract; final String fName = name; return new IndexedFilter() { @Override public boolean matches(Descriptor d) { if (qualifiers.isEmpty()) return true; return d.getQualifiers().containsAll(qualifiers); } @Override public String getAdvertisedContract() { return fContract; } @Override public String getName() { return fName; } @Override public String toString() { String cField = (fContract == null) ? "" : fContract; String nField = (fName == null) ? "" : ";name=" + fName; StringBuffer sb = new StringBuffer(); for (String q : qualifiers) { sb.append(";qualifier=" + q); } return "TokenizedFilter(" + cField + nField + sb.toString() + ")"; } }; } /** * This method creates a filter that will match one and only one descriptor. The passed * in descriptor must have both its serviceID and locatorId filled in, or else this * method will throw an IllegalArgumentException * * @param descriptor The descriptor from which to create a filter * @return A filter to use that will match this descriptor exactly */ public static IndexedFilter createSpecificDescriptorFilter(Descriptor descriptor) { String contract = ServiceLocatorUtilities.getBestContract(descriptor); String name = descriptor.getName(); if (descriptor.getServiceId() == null) { throw new IllegalArgumentException("The descriptor must have a specific service ID"); } if (descriptor.getLocatorId() == null) { throw new IllegalArgumentException("The descriptor must have a specific locator ID"); } return new SpecificFilterImpl(contract, name, descriptor.getServiceId(), descriptor.getLocatorId()); } /** * Returns a filter that will return true an IndexedFilter that will match * the {@link DescriptorImpl#equals(Object)} return * * @param descriptorImpl A non-null Descriptor to compare against other Descriptors * @param deepCopy If true then a copy will be made of the descriptorImpl to protect the filter * from the case where the incoming descriptorImpl may change. If false then the user * must ensure that the fields of the descriptorImpl cannot change after the filter is returned. * @return An IndexedFilter that can be used to determine equality with the given filter * based on the fields of the Descriptor */ public static IndexedFilter createDescriptorFilter(Descriptor descriptorImpl, boolean deepCopy) { final Descriptor filterDescriptor = (deepCopy) ? new DescriptorImpl(descriptorImpl) : descriptorImpl ; return new IndexedFilter() { @Override public boolean matches(Descriptor d) { return DescriptorImpl.descriptorEquals(filterDescriptor, d); } @Override public String getAdvertisedContract() { Set contracts = filterDescriptor.getAdvertisedContracts(); if (contracts == null || contracts.isEmpty()) return null; return contracts.iterator().next(); } @Override public String getName() { return filterDescriptor.getName(); } }; } /** * Returns a filter that will return true an IndexedFilter that will match * the {@link DescriptorImpl#equals(Object)} return * * @param descriptorImpl A non-null Descriptor to compare against other Descriptors * @return An IndexedFilter that can be used to determine equality with the given filter * based on the fields of the Descriptor */ public static IndexedFilter createDescriptorFilter(Descriptor descriptorImpl) { return createDescriptorFilter(descriptorImpl, true); } /** * Returns a filter of type Descriptor that matches * all descriptors * * @return A filter that matches all descriptors */ public static Filter allFilter() { return StarFilter.getDescriptorFilter(); } /** * This method links an implementation class with a {@link DescriptorBuilder}, to * be used to further build the {@link Descriptor}. * * @param implementationClass The fully qualified name of the implementation * class to be associated with the DescriptorBuilder. * @param addToContracts if true, this implementation class will be added to the * list of contracts * * @return A {@link DescriptorBuilder} that can be used to further build up the * {@link Descriptor} * @throws IllegalArgumentException if implementationClass is null */ public static DescriptorBuilder link(String implementationClass, boolean addToContracts) throws IllegalArgumentException { if (implementationClass == null) throw new IllegalArgumentException(); return new DescriptorBuilderImpl(implementationClass, addToContracts); } /** * This method links an implementation class with a {@link DescriptorBuilder}, to * be used to further build the {@link Descriptor}. This method will automatically * put the implementationClass into the list of advertised contracts. * * @param implementationClass The fully qualified name of the implementation * class to be associated with the PredicateBuilder. * * @return A {@link DescriptorBuilder} that can be used to further build up the * {@link Descriptor} * @throws IllegalArgumentException if implementationClass is null */ public static DescriptorBuilder link(String implementationClass) throws IllegalArgumentException { return link(implementationClass, true); } /** * This method links an implementation class with a {@link DescriptorBuilder}, to * be used to further build the {@link Descriptor} * * @param implementationClass The implementation class to be associated * with the {@link DescriptorBuilder}. * @param addToContracts true if this impl class should be automatically added to * the list of contracts * @return A {@link DescriptorBuilder} that can be used to further build up the * {@link Descriptor} * @throws IllegalArgumentException if implementationClass is null */ public static DescriptorBuilder link(Class implementationClass, boolean addToContracts) throws IllegalArgumentException { if (implementationClass == null) throw new IllegalArgumentException(); DescriptorBuilder builder = link(implementationClass.getName(), addToContracts); return builder; } /** * This method links an implementation class with a {@link DescriptorBuilder}, to * be used to further build the {@link Descriptor}. * * @param implementationClass The implementation class to be associated * with the {@link DescriptorBuilder}. * @return A {@link DescriptorBuilder} that can be used to further build up the * {@link Descriptor} * @throws IllegalArgumentException if implementationClass is null */ public static DescriptorBuilder link(Class implementationClass) throws IllegalArgumentException { if (implementationClass == null) throw new IllegalArgumentException(); boolean isFactory = (Factory.class.isAssignableFrom(implementationClass)); DescriptorBuilder db = link(implementationClass, !isFactory); return db; } /** * This method creates an {@link ActiveDescriptorBuilder}, whose job it * is to create an unreified {@link ActiveDescriptor}. The implementation * class given will NOT automatically be added to the set of contracts * of the {@link ActiveDescriptor}. * * @param implementationClass The implementation class to be associated * with the {@link ActiveDescriptorBuilder}. * @return A {@link ActiveDescriptorBuilder} that can be used to further build up the * {@link Descriptor} * @throws IllegalArgumentException if implementationClass is null */ public static ActiveDescriptorBuilder activeLink(Class implementationClass) throws IllegalArgumentException { if (implementationClass == null) throw new IllegalArgumentException(); return new ActiveDescriptorBuilderImpl(implementationClass); } /** * This creates a descriptor that will always return the given object. The * set of types in the advertised contracts will contain the class of the * constant along with:
    *
  • Any superclass of the constant marked with {@link Contract}
  • *
  • Any interface of the constant marked with {@link Contract}
  • *
* * @param constant The non-null constant that should always be returned from * the create method of this ActiveDescriptor. * @return The descriptor returned can be used in calls to * DynamicConfiguration.addActiveDescriptor * @throws IllegalArgumentException if constant is null */ public static AbstractActiveDescriptor createConstantDescriptor(T constant) { if (constant == null) throw new IllegalArgumentException(); Set contracts; Class cClass = constant.getClass(); ContractsProvided provided = cClass.getAnnotation(ContractsProvided.class); if (provided != null) { contracts = new HashSet(); for (Class specified : provided.value()) { contracts.add(specified); } } else { contracts = ReflectionHelper.getAdvertisedTypesFromObject(constant, Contract.class); } return createConstantDescriptor(constant, ReflectionHelper.getName(constant.getClass()), contracts.toArray(new Type[contracts.size()])); } /** * Gets the rank from the given class * * @param fromClass The class to get the rank from. Will also check all * superclasses * @return The rank this class should initially have, or 0 if there is * no Rank annotation on this class or all its superclasses */ public static int getRank(Class fromClass) { while (fromClass != null && !Object.class.equals(fromClass)) { Rank rank = fromClass.getAnnotation(Rank.class); if (rank != null) { return rank.value(); } fromClass = fromClass.getSuperclass(); } return 0; } /** * This creates a descriptor that will always return the given object. * The advertised contracts is given in the incoming parameter and the * name on the descriptor also comes from the incoming parameter. * * @param constant The non-null constant that should always be returned from * the create method of this ActiveDescriptor. * @param name The possibly null name that should be associated with this constant descriptor * @param contracts The possibly empty set of contracts that should be associated with this * descriptor * @return The descriptor returned can be used in calls to * DynamicConfiguration.addActiveDescriptor * @throws IllegalArgumentException if constant is null */ public static AbstractActiveDescriptor createConstantDescriptor(T constant, String name, Type... contracts) { if (constant == null) throw new IllegalArgumentException(); Annotation scope = ReflectionHelper.getScopeAnnotationFromObject(constant); Class scopeClass = (scope == null) ? PerLookup.class : scope.annotationType(); Set qualifiers = ReflectionHelper.getQualifiersFromObject(constant); Map> metadata = new HashMap>(); if (scope != null) { getMetadataValues(scope, metadata); } for (Annotation qualifier : qualifiers) { getMetadataValues(qualifier, metadata); } Set contractsAsSet; if (contracts.length <= 0) { contractsAsSet = ReflectionHelper.getAdvertisedTypesFromObject(constant, Contract.class); } else { contractsAsSet = new LinkedHashSet(); for (Type cType : contracts) { contractsAsSet.add(cType); } } Boolean proxy = null; UseProxy up = constant.getClass().getAnnotation(UseProxy.class); if (up != null) { proxy = up.value(); } Boolean proxyForSameScope = null; ProxyForSameScope pfss = constant.getClass().getAnnotation(ProxyForSameScope.class); if (pfss != null) { proxyForSameScope = pfss.value(); } DescriptorVisibility visibility = DescriptorVisibility.NORMAL; Visibility vi = constant.getClass().getAnnotation(Visibility.class); if (vi != null) { visibility = vi.value(); } String classAnalysisName = null; Service service = constant.getClass().getAnnotation(Service.class); if (service != null) { classAnalysisName = service.analyzer(); } int rank = getRank(constant.getClass()); return new ConstantActiveDescriptor( constant, contractsAsSet, scopeClass, name, qualifiers, visibility, proxy, proxyForSameScope, classAnalysisName, metadata, rank); } /** * This returns a DescriptorImpl based on the given class. The returned * descriptor will include the class itself as an advertised contract and * all implemented interfaces that are marked @Contract * * @param clazz The class to analyze * @return The DescriptorImpl corresponding to this class */ public static DescriptorImpl createDescriptorFromClass(Class clazz) { if (clazz == null) return new DescriptorImpl(); Set contracts = ReflectionHelper.getContractsFromClass(clazz, Contract.class); String name = ReflectionHelper.getName(clazz); String scope = ReflectionHelper.getScopeFromClass(clazz, ServiceLocatorUtilities.getPerLookupAnnotation()).annotationType().getName(); Set qualifiers = ReflectionHelper.getQualifiersFromClass(clazz); DescriptorType type = DescriptorType.CLASS; if (Factory.class.isAssignableFrom(clazz)) { type = DescriptorType.PROVIDE_METHOD; } Boolean proxy = null; UseProxy up = clazz.getAnnotation(UseProxy.class); if (up != null) { proxy = Boolean.valueOf(up.value()); } Boolean proxyForSameScope = null; ProxyForSameScope pfss = clazz.getAnnotation(ProxyForSameScope.class); if (pfss != null) { proxyForSameScope = Boolean.valueOf(pfss.value()); } DescriptorVisibility visibility = DescriptorVisibility.NORMAL; Visibility vi = clazz.getAnnotation(Visibility.class); if (vi != null) { visibility = vi.value(); } int rank = getRank(clazz); // TODO: Can we get metadata from @Service? return new DescriptorImpl( contracts, name, scope, clazz.getName(), new HashMap>(), qualifiers, type, visibility, null, rank, proxy, proxyForSameScope, null, null, null); } /** * Makes a deep copy of the incoming descriptor * * @param copyMe The descriptor to copy * @return A new descriptor with all fields copied */ public static DescriptorImpl deepCopyDescriptor(Descriptor copyMe) { return new DescriptorImpl(copyMe); } /** * This is a helper method that gets the metadata values from the * {@link Metadata} annotations found in an annotation. * * @param annotation The annotation to find {@link Metadata} values * from. May not be null. * @param metadata A non-null metadata map. The values found in the * annotation will be added to this metadata map * @throws IllegalArgumentException if annotation or metadata is null * @throws MultiException if there was an error invoking the methods of the annotation */ public static void getMetadataValues(Annotation annotation, Map> metadata) { if (annotation == null || metadata == null) { throw new IllegalArgumentException(); } final Class annotationClass = annotation.annotationType(); Method annotationMethods[] = AccessController.doPrivileged(new PrivilegedAction() { @Override public Method[] run() { return annotationClass.getDeclaredMethods(); } }); for (Method annotationMethod : annotationMethods) { Metadata metadataAnno = annotationMethod.getAnnotation(Metadata.class); if (metadataAnno == null) continue; String key = metadataAnno.value(); Object addMe; try { addMe = ReflectionHelper.invoke(annotation, annotationMethod, new Object[0], false); } catch (Throwable th) { throw new MultiException(th); } if (addMe == null) continue; String addMeString; if (addMe instanceof Class) { addMeString = ((Class) addMe).getName(); } else if (addMe.getClass().isArray()) { int length = Array.getLength(addMe); for (int lcv = 0; lcv < length; lcv++) { Object iValue = Array.get(addMe, lcv); if (iValue == null) continue; if (iValue instanceof Class) { String cName = ((Class) iValue).getName(); ReflectionHelper.addMetadata(metadata, key, cName); } else { ReflectionHelper.addMetadata(metadata, key, iValue.toString()); } } addMeString = null; } else { addMeString = addMe.toString(); } if (addMeString != null) { ReflectionHelper.addMetadata(metadata, key, addMeString); } } } /** * Creates a ServiceHandle that will always return the given object from * the {@link ServiceHandle#getService()} method. The {@link ServiceHandle#getActiveDescriptor()} * will return null and the {@link ServiceHandle#destroy()} method will * do nothing * * @param obj The object to be associated with this ServiceHandle. May be null * @return A {@link ServiceHandle} that will always return the given value */ public static ServiceHandle createConstantServiceHandle(final T obj) { return new ServiceHandle() { private Object serviceData; @Override public T getService() { return obj; } @Override public ActiveDescriptor getActiveDescriptor() { return null; } @Override public boolean isActive() { return true; } @Override public void close() { // Do nothing } @Override public synchronized void setServiceData(Object serviceData) { this.serviceData = serviceData; } @Override public synchronized Object getServiceData() { return serviceData; } @Override public List> getSubHandles() { return Collections.emptyList(); } }; } /** * Determines if the given descriptor matches the given filter. A null * filter matches every descriptor. Takes into account if the {@link Filter} * implements {@link IndexedFilter}. * * @param baseDescriptor The non-null descriptor to match the filter against * @param filter The filter to match. If null will return true * @return true if baseDescriptor matches, false otherwise */ public static boolean filterMatches(final Descriptor baseDescriptor, final Filter filter) { if (baseDescriptor == null) throw new IllegalArgumentException(); if (filter == null) return true; if (filter instanceof IndexedFilter) { IndexedFilter indexedFilter = (IndexedFilter) filter; String indexContract = indexedFilter.getAdvertisedContract(); if (indexContract != null) { if (!baseDescriptor.getAdvertisedContracts().contains(indexContract)) { return false; } } String indexName = indexedFilter.getName(); if (indexName != null) { if (baseDescriptor.getName() == null) return false; if (!indexName.equals(baseDescriptor.getName())) { return false; } } // After all that we can run the match method! } return filter.matches(baseDescriptor); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy