View Javadoc
1   package edu.internet2.middleware.grouper.pspng;
2   
3   /*******************************************************************************
4    * Copyright 2015 Internet2
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   ******************************************************************************/
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.util.concurrent.ConcurrentMap;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import edu.internet2.middleware.grouper.app.loader.GrouperLoaderConfig;
30  
31  
32  /**
33   * This class helps construct and cache Provisioners. 
34   * 
35   * 1) Because Provisioners have lots of state (caches and configurations), we don't
36   * want to construct them each time the changelog sends us events. Therefore, this
37   * class keeps a (static) HashMap that allows all the changelog's event batches to
38   * be sent to the same Provisioner instance.
39   * 
40   * 2) There's a bunch of Reflection to get Provisioners and ProvisionerConfiguration
41   * constructed. This class encapsulates these steps.
42   * 
43   * @author bert
44   *
45   */
46  public class ProvisionerFactory {
47    private final static Logger LOG = LoggerFactory.getLogger(ProvisionerFactory.class);
48    final static String PROVISIONER_TYPE_PROPERTY_NAME = "type";
49    
50  
51    // Indexed by configName (ie, the configuration paragraph found in grouper_loader.properties
52    private static Map<String, Provisioner> incrementalProvisioners=new ConcurrentHashMap<String, Provisioner>();
53  
54    private static ConcurrentMap<String, ProvisionerCoordinator> provisionerCoordinators=new ConcurrentHashMap<>();
55  
56  
57    public static Provisioner getIncrementalProvisioner(String configName) throws PspException {
58      synchronized (incrementalProvisioners) {
59        if ( !incrementalProvisioners.containsKey(configName) )
60        {
61          Provisioner provisioner;
62          try {
63            provisioner = ProvisionerFactory.createProvisioner(configName, false);
64            incrementalProvisioners.put(configName, provisioner);
65          } catch (PspException e) {
66            LOG.error("Unable to create incremental provisioner {}", configName, e);
67            throw e;
68          }
69        }
70      }
71      
72      return incrementalProvisioners.get(configName);
73    }
74  
75  
76  
77    public static ProvisionerCoordinator getProvisionerCoordinator(Provisioner<?,?,?> provisioner) {
78      String provisionerConfigName = provisioner.getConfigName();
79  
80      if ( !provisionerCoordinators.containsKey(provisionerConfigName) ) {
81        provisionerCoordinators.putIfAbsent(provisionerConfigName, new ProvisionerCoordinator(provisioner));
82      }
83  
84      return provisionerCoordinators.get(provisionerConfigName);
85    }
86  
87    /**
88     * This constructs a provisioner based on the properties found for provisioner 'configName'
89     *
90     * This should only be called internally and from FullSyncProvisionerFactory. This is public to enable
91     * gsh to construct a provisioner for diagnostic purposes.
92     * @param configName
93     * @param fullSyncMode
94     * @return
95     * @throws PspException
96     */
97    public static Provisioner createProvisioner(String configName, boolean fullSyncMode) throws PspException {
98      final String qualifiedParameterNamespace = ProvisionerConfiguration.PARAMETER_NAMESPACE + configName + ".";
99    
100     LOG.info("Constructing provisioner: {}", configName);
101     String typeName = GrouperLoaderConfig.retrieveConfig().propertyValueStringRequired(qualifiedParameterNamespace + PROVISIONER_TYPE_PROPERTY_NAME);
102     
103     // TODO: Someday allow types that are not java classes, either via code-based shortcuts
104     // or via a lookup table/file (eg, type=ldap-groups instead of edu.internet2.....LdapGroupProvisioner)
105     String className = typeName;
106     try {
107       Class<? extends Provisioner> provisionerClass = (Class<? extends Provisioner>) Class.forName(className);
108       Method getPropertyClassMethod = provisionerClass.getMethod("getPropertyClass");
109       
110       Class<? extends ProvisionerConfiguration> propertyClass = (Class<? extends ProvisionerConfiguration>) getPropertyClassMethod.invoke(null);
111       
112       Constructor<? extends ProvisionerConfiguration> propertyConstructor = propertyClass.getConstructor(String.class);
113       
114       ProvisionerConfiguration properties = propertyConstructor.newInstance(configName);
115       
116       properties.readConfiguration();
117       
118       Constructor<? extends Provisioner> provisionerConstructor = provisionerClass.getConstructor(String.class, propertyClass, Boolean.TYPE);
119       Provisioner provisioner = provisionerConstructor.newInstance(configName, properties, fullSyncMode);
120       return provisioner;
121     } catch (ClassNotFoundException e) {
122       Provisioner.STATIC_LOG.error("Unable to find provisioner class: {}", className);
123       throw new PspException("Unknown provisioner class %s", className);
124     } catch (IllegalAccessException e) {
125       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
126       throw new PspException("IllegalAccessException while constructing provisioner & properties: %s", className);
127     } catch (IllegalArgumentException e) {
128       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
129       throw new PspException("IllegalArgumentException while constructing provisioner & properties: %s", className);
130     } catch (InvocationTargetException e) {
131       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
132       throw new PspException("Problem while constructing provisioner & properties: %s: %s", className, e.getCause().getMessage());
133     } catch (NoSuchMethodException e) {
134       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
135       throw new PspException("NoSuchMethodException while constructing provisioner & properties: %s: %s", className, e.getMessage());
136     } catch (SecurityException e) {
137       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
138       throw new PspException("SecurityException while constructing provisioner & properties: %s: %s", className, e.getMessage());
139     } catch (InstantiationException e) {
140       Provisioner.STATIC_LOG.error("Problem constructing provisioner & properties: {}", className, e);
141       throw new PspException("InstantiationException while constructing provisioner & properties: %s: %s", className, e.getMessage());
142     }
143   
144     
145   }
146   
147 
148 }