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