1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.spring.implementation;
20
21 import groovy.lang.GroovyClassLoader;
22 import java.lang.reflect.Modifier;
23 import java.lang.reflect.ParameterizedType;
24 import java.lang.reflect.Type;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.function.Consumer;
30 import java.util.function.Supplier;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.apache.syncope.common.lib.command.CommandArgs;
33 import org.apache.syncope.common.lib.policy.AccountRuleConf;
34 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
35 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
36 import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
37 import org.apache.syncope.common.lib.report.ReportConf;
38 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
39 import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
40 import org.apache.syncope.core.persistence.api.entity.Implementation;
41 import org.apache.syncope.core.provisioning.api.ImplementationLookup;
42 import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
43 import org.apache.syncope.core.provisioning.api.rules.AccountRule;
44 import org.apache.syncope.core.provisioning.api.rules.PasswordRule;
45 import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule;
46 import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule;
47 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
48 import org.apache.syncope.core.spring.ApplicationContextProvider;
49 import org.springframework.beans.factory.support.AbstractBeanDefinition;
50
51 public final class ImplementationManager {
52
53 private static final GroovyClassLoader GROOVY_CLASSLOADER = new GroovyClassLoader();
54
55 private static final Map<String, Class<?>> CLASS_CACHE = Collections.synchronizedMap(new HashMap<>());
56
57 @SuppressWarnings("unchecked")
58 public static Optional<ReportJobDelegate> buildReportJobDelegate(
59 final Implementation impl,
60 final Supplier<ReportJobDelegate> cacheGetter,
61 final Consumer<ReportJobDelegate> cachePutter)
62 throws ClassNotFoundException {
63
64 switch (impl.getEngine()) {
65 case GROOVY:
66 return Optional.of(build(impl, cacheGetter, cachePutter));
67
68 case JAVA:
69 default:
70 ReportConf conf = POJOHelper.deserialize(impl.getBody(), ReportConf.class);
71 Class<ReportJobDelegate> clazz =
72 (Class<ReportJobDelegate>) ApplicationContextProvider.getApplicationContext().
73 getBean(ImplementationLookup.class).getReportClass(conf.getClass());
74
75 if (clazz == null) {
76 return Optional.empty();
77 }
78
79 ReportJobDelegate report = build(clazz, true, cacheGetter, cachePutter);
80 report.setConf(conf);
81 return Optional.of(report);
82 }
83 }
84
85 @SuppressWarnings("unchecked")
86 public static Optional<AccountRule> buildAccountRule(
87 final Implementation impl,
88 final Supplier<AccountRule> cacheGetter,
89 final Consumer<AccountRule> cachePutter)
90 throws ClassNotFoundException {
91
92 switch (impl.getEngine()) {
93 case GROOVY:
94 return Optional.of(build(impl, cacheGetter, cachePutter));
95
96 case JAVA:
97 default:
98 AccountRuleConf conf = POJOHelper.deserialize(impl.getBody(), AccountRuleConf.class);
99 Class<AccountRule> clazz = (Class<AccountRule>) ApplicationContextProvider.getApplicationContext().
100 getBean(ImplementationLookup.class).getAccountRuleClass(conf.getClass());
101
102 if (clazz == null) {
103 return Optional.empty();
104 }
105
106 AccountRule rule = build(clazz, true, cacheGetter, cachePutter);
107 rule.setConf(conf);
108 return Optional.of(rule);
109 }
110 }
111
112 @SuppressWarnings("unchecked")
113 public static Optional<PasswordRule> buildPasswordRule(
114 final Implementation impl,
115 final Supplier<PasswordRule> cacheGetter,
116 final Consumer<PasswordRule> cachePutter)
117 throws ClassNotFoundException {
118
119 switch (impl.getEngine()) {
120 case GROOVY:
121 return Optional.of(build(impl, cacheGetter, cachePutter));
122
123 case JAVA:
124 default:
125 PasswordRuleConf conf = POJOHelper.deserialize(impl.getBody(), PasswordRuleConf.class);
126 Class<PasswordRule> clazz = (Class<PasswordRule>) ApplicationContextProvider.getApplicationContext().
127 getBean(ImplementationLookup.class).getPasswordRuleClass(conf.getClass());
128
129 if (clazz == null) {
130 return Optional.empty();
131 }
132
133 PasswordRule rule = build(clazz, true, cacheGetter, cachePutter);
134 rule.setConf(conf);
135 return Optional.of(rule);
136 }
137 }
138
139 @SuppressWarnings("unchecked")
140 public static Optional<PullCorrelationRule> buildPullCorrelationRule(
141 final Implementation impl,
142 final Supplier<PullCorrelationRule> cacheGetter,
143 final Consumer<PullCorrelationRule> cachePutter)
144 throws ClassNotFoundException {
145
146 switch (impl.getEngine()) {
147 case GROOVY:
148 return Optional.of(build(impl, cacheGetter, cachePutter));
149
150 case JAVA:
151 default:
152 PullCorrelationRuleConf conf = POJOHelper.deserialize(impl.getBody(), PullCorrelationRuleConf.class);
153 Class<PullCorrelationRule> clazz =
154 (Class<PullCorrelationRule>) ApplicationContextProvider.getApplicationContext().
155 getBean(ImplementationLookup.class).getPullCorrelationRuleClass(conf.getClass());
156
157 if (clazz == null) {
158 return Optional.empty();
159 }
160
161 PullCorrelationRule rule = build(clazz, true, cacheGetter, cachePutter);
162 rule.setConf(conf);
163 return Optional.of(rule);
164 }
165 }
166
167 @SuppressWarnings("unchecked")
168 public static Optional<PushCorrelationRule> buildPushCorrelationRule(
169 final Implementation impl,
170 final Supplier<PushCorrelationRule> cacheGetter,
171 final Consumer<PushCorrelationRule> cachePutter)
172 throws ClassNotFoundException {
173
174 switch (impl.getEngine()) {
175 case GROOVY:
176 return Optional.of(build(impl, cacheGetter, cachePutter));
177
178 case JAVA:
179 default:
180 PushCorrelationRuleConf conf = POJOHelper.deserialize(impl.getBody(), PushCorrelationRuleConf.class);
181 Class<PushCorrelationRule> clazz =
182 (Class<PushCorrelationRule>) ApplicationContextProvider.getApplicationContext().
183 getBean(ImplementationLookup.class).getPushCorrelationRuleClass(conf.getClass());
184
185 if (clazz == null) {
186 return Optional.empty();
187 }
188
189 PushCorrelationRule rule = build(clazz, true, cacheGetter, cachePutter);
190 rule.setConf(conf);
191 return Optional.of(rule);
192 }
193 }
194
195 @SuppressWarnings("unchecked")
196 private static Class<? extends CommandArgs> findCommandArgsClass(final Type type) {
197 if (type.getTypeName().startsWith(
198 ImplementationTypesHolder.getInstance().getValues().get(IdRepoImplementationType.COMMAND) + "<")) {
199
200 return (Class<? extends CommandArgs>) ((ParameterizedType) type).getActualTypeArguments()[0];
201 }
202
203 if (type instanceof Class) {
204 for (Type i : ((Class) type).getGenericInterfaces()) {
205 Class<? extends CommandArgs> r = findCommandArgsClass(i);
206 if (r != null) {
207 return r;
208 }
209 }
210 }
211
212 return null;
213 }
214
215 public static CommandArgs emptyArgs(final Implementation impl) throws Exception {
216 if (!IdRepoImplementationType.COMMAND.equals(impl.getType())) {
217 throw new IllegalArgumentException("This method can be only called on implementations");
218 }
219
220 Class<Object> commandClass = getClass(impl).getLeft();
221
222 Class<? extends CommandArgs> commandArgsClass = findCommandArgsClass(commandClass);
223 if (commandArgsClass != null
224 && (commandArgsClass.getEnclosingClass() == null
225 || Modifier.isStatic(commandArgsClass.getModifiers()))) {
226
227 return commandArgsClass.getDeclaredConstructor().newInstance();
228 }
229
230 throw new IllegalArgumentException(
231 CommandArgs.class.getName() + " shall be either declared as independent or nested static");
232 }
233
234 @SuppressWarnings("unchecked")
235 private static <T> Pair<Class<T>, Boolean> getClass(final Implementation impl) throws ClassNotFoundException {
236 if (CLASS_CACHE.containsKey(impl.getKey())) {
237 return Pair.of((Class<T>) CLASS_CACHE.get(impl.getKey()), true);
238 }
239
240 Class<?> clazz;
241 switch (impl.getEngine()) {
242 case GROOVY:
243 clazz = GROOVY_CLASSLOADER.parseClass(impl.getBody());
244 break;
245
246 case JAVA:
247 default:
248 clazz = Class.forName(impl.getBody());
249 }
250
251 CLASS_CACHE.put(impl.getKey(), clazz);
252 return Pair.of((Class<T>) clazz, false);
253 }
254
255 @SuppressWarnings("unchecked")
256 public static <T> T build(final Implementation impl) throws ClassNotFoundException {
257 return (T) ApplicationContextProvider.getBeanFactory().
258 createBean(getClass(impl).getLeft(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
259 }
260
261 @SuppressWarnings("unchecked")
262 private static <T> T build(
263 final Class<T> clazz,
264 final boolean classCached,
265 final Supplier<T> cacheGetter,
266 final Consumer<T> cachePutter) {
267
268 boolean perContext = Optional.ofNullable(clazz.getAnnotation(SyncopeImplementation.class)).
269 map(ann -> ann.scope() == InstanceScope.PER_CONTEXT).
270 orElse(true);
271 T instance = null;
272 if (perContext && classCached) {
273 instance = cacheGetter.get();
274 }
275 if (instance == null) {
276 instance = (T) ApplicationContextProvider.getBeanFactory().
277 createBean(clazz, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
278
279 if (perContext) {
280 cachePutter.accept(instance);
281 }
282 }
283
284 return instance;
285 }
286
287 public static <T> T build(final Implementation impl, final Supplier<T> cacheGetter, final Consumer<T> cachePutter)
288 throws ClassNotFoundException {
289
290 Pair<Class<T>, Boolean> clazz = getClass(impl);
291
292 return build(clazz.getLeft(), clazz.getRight(), cacheGetter, cachePutter);
293 }
294
295 public static Class<?> purge(final String implementation) {
296 return CLASS_CACHE.remove(implementation);
297 }
298
299 private ImplementationManager() {
300
301 }
302 }