View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java;
20  
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Optional;
27  import java.util.Set;
28  import org.apache.syncope.common.lib.to.ConnInstanceTO;
29  import org.apache.syncope.common.lib.types.ConnConfProperty;
30  import org.apache.syncope.common.lib.types.ConnectorCapability;
31  import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
32  import org.apache.syncope.core.persistence.api.dao.RealmDAO;
33  import org.apache.syncope.core.persistence.api.entity.ConnInstance;
34  import org.apache.syncope.core.persistence.api.entity.EntityFactory;
35  import org.apache.syncope.core.persistence.api.entity.ExternalResource;
36  import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
37  import org.apache.syncope.core.provisioning.api.Connector;
38  import org.apache.syncope.core.provisioning.api.ConnectorManager;
39  import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
40  import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils;
41  import org.apache.syncope.core.spring.ApplicationContextProvider;
42  import org.apache.syncope.core.spring.security.AuthContextUtils;
43  import org.identityconnectors.common.l10n.CurrentLocale;
44  import org.identityconnectors.framework.api.ConnectorFacadeFactory;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  public class DefaultConnectorManager implements ConnectorManager {
50  
51      protected static final Logger LOG = LoggerFactory.getLogger(ConnectorManager.class);
52  
53      protected static String getBeanName(final ExternalResource resource) {
54          return String.format("connInstance-%s-%S-%s",
55                  AuthContextUtils.getDomain(), resource.getConnector().getKey(), resource.getKey());
56      }
57  
58      protected final ConnIdBundleManager connIdBundleManager;
59  
60      protected final RealmDAO realmDAO;
61  
62      protected final ExternalResourceDAO resourceDAO;
63  
64      protected final ConnInstanceDataBinder connInstanceDataBinder;
65  
66      protected final AsyncConnectorFacade asyncFacade;
67  
68      protected final EntityFactory entityFactory;
69  
70      public DefaultConnectorManager(
71              final ConnIdBundleManager connIdBundleManager,
72              final RealmDAO realmDAO,
73              final ExternalResourceDAO resourceDAO,
74              final ConnInstanceDataBinder connInstanceDataBinder,
75              final AsyncConnectorFacade asyncFacade,
76              final EntityFactory entityFactory) {
77  
78          this.connIdBundleManager = connIdBundleManager;
79          this.realmDAO = realmDAO;
80          this.resourceDAO = resourceDAO;
81          this.connInstanceDataBinder = connInstanceDataBinder;
82          this.asyncFacade = asyncFacade;
83          this.entityFactory = entityFactory;
84      }
85  
86      @Override
87      public Optional<Connector> readConnector(final ExternalResource resource) {
88          return Optional.ofNullable((Connector) ApplicationContextProvider.getBeanFactory().
89                  getSingleton(getBeanName(resource)));
90      }
91  
92      @Override
93      public Connector getConnector(final ExternalResource resource) {
94          // Try to re-create connector bean from underlying resource (useful for managing failover scenarios)
95          return readConnector(resource).orElseGet(() -> {
96              registerConnector(resource);
97              return (Connector) ApplicationContextProvider.getBeanFactory().getSingleton(getBeanName(resource));
98          });
99      }
100 
101     @Override
102     public ConnInstance buildConnInstanceOverride(
103             final ConnInstanceTO connInstance,
104             final Collection<ConnConfProperty> confOverride,
105             final Optional<Collection<ConnectorCapability>> capabilitiesOverride) {
106 
107         ConnInstance override = entityFactory.newEntity(ConnInstance.class);
108         override.setAdminRealm(realmDAO.findByFullPath(connInstance.getAdminRealm()));
109         override.setConnectorName(connInstance.getConnectorName());
110         override.setDisplayName(connInstance.getDisplayName());
111         override.setBundleName(connInstance.getBundleName());
112         override.setVersion(connInstance.getVersion());
113         override.setLocation(connInstance.getLocation());
114         override.setConf(connInstance.getConf());
115         override.getCapabilities().addAll(connInstance.getCapabilities());
116         override.setConnRequestTimeout(connInstance.getConnRequestTimeout());
117 
118         Map<String, ConnConfProperty> overridable = new HashMap<>();
119         Set<ConnConfProperty> conf = new HashSet<>();
120 
121         override.getConf().forEach(prop -> {
122             if (prop.isOverridable()) {
123                 overridable.put(prop.getSchema().getName(), prop);
124             } else {
125                 conf.add(prop);
126             }
127         });
128 
129         // add override properties
130         confOverride.stream().
131                 filter(prop -> overridable.containsKey(prop.getSchema().getName()) && !prop.getValues().isEmpty()).
132                 forEach(prop -> {
133                     conf.add(prop);
134                     overridable.remove(prop.getSchema().getName());
135                 });
136 
137         // add override properties not substituted
138         conf.addAll(overridable.values());
139 
140         override.setConf(conf);
141 
142         // replace capabilities
143         capabilitiesOverride.ifPresent(capabilities -> {
144             override.getCapabilities().clear();
145             override.getCapabilities().addAll(capabilities);
146         });
147 
148         if (connInstance.getPoolConf() != null) {
149             override.setPoolConf(
150                     ConnPoolConfUtils.getConnPoolConf(connInstance.getPoolConf(), entityFactory.newConnPoolConf()));
151         }
152 
153         return override;
154     }
155 
156     @Override
157     public Connector createConnector(final ConnInstance connInstance) {
158         return new ConnectorFacadeProxy(connInstance, asyncFacade);
159     }
160 
161     @Override
162     public void registerConnector(final ExternalResource resource) {
163         String beanName = getBeanName(resource);
164 
165         if (ApplicationContextProvider.getBeanFactory().containsSingleton(beanName)) {
166             unregisterConnector(beanName);
167         }
168 
169         ConnInstance connInstance = buildConnInstanceOverride(
170                 connInstanceDataBinder.getConnInstanceTO(resource.getConnector()),
171                 resource.getConfOverride(),
172                 resource.isOverrideCapabilities() ? Optional.of(resource.getCapabilitiesOverride()) : Optional.empty());
173         Connector connector = createConnector(connInstance);
174         LOG.debug("Connector to be registered: {}", connector);
175 
176         ApplicationContextProvider.getBeanFactory().registerSingleton(beanName, connector);
177         LOG.debug("Successfully registered bean {}", beanName);
178     }
179 
180     protected void unregisterConnector(final String id) {
181         ApplicationContextProvider.getBeanFactory().destroySingleton(id);
182     }
183 
184     @Override
185     public void unregisterConnector(final ExternalResource resource) {
186         String beanName = getBeanName(resource);
187         if (ApplicationContextProvider.getBeanFactory().containsSingleton(beanName)) {
188             unregisterConnector(beanName);
189         }
190     }
191 
192     @Transactional(readOnly = true)
193     @Override
194     public void load() {
195         // This is needed in order to avoid encoding problems when sending error messages via REST
196         CurrentLocale.set(Locale.ENGLISH);
197 
198         // Load all connector bundles
199         connIdBundleManager.getConnManagers();
200 
201         // Load all resource-specific connectors
202         int connectors = 0;
203         for (ExternalResource resource : resourceDAO.findAll()) {
204             LOG.info("Registering resource-connector pair {}-{}", resource, resource.getConnector());
205             try {
206                 registerConnector(resource);
207 
208                 connectors++;
209             } catch (Exception e) {
210                 LOG.error("While registering resource-connector pair {}-{}", resource, resource.getConnector(), e);
211             }
212         }
213 
214         LOG.info("Done loading {} connectors", connectors);
215     }
216 
217     @Transactional(readOnly = true)
218     @Override
219     public void unload() {
220         int connectors = 0;
221         for (ExternalResource resource : resourceDAO.findAll()) {
222             String beanName = getBeanName(resource);
223             if (ApplicationContextProvider.getBeanFactory().containsSingleton(beanName)) {
224                 LOG.info("Unegistering resource-connector pair {}-{}", resource, resource.getConnector());
225 
226                 getConnector(resource).dispose();
227                 unregisterConnector(beanName);
228 
229                 connectors++;
230             }
231         }
232 
233         LOG.info("Done unloading {} connectors", connectors);
234 
235         ConnectorFacadeFactory.getInstance().dispose();
236         connIdBundleManager.resetConnManagers();
237         LOG.info("All connector resources disposed");
238     }
239 }