1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.starter.actuate;
20
21 import java.lang.management.ManagementFactory;
22 import java.lang.management.OperatingSystemMXBean;
23 import java.lang.management.RuntimeMXBean;
24 import java.net.InetAddress;
25 import java.net.URI;
26 import java.net.UnknownHostException;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.Set;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import java.util.stream.Collectors;
34 import javax.servlet.http.HttpServletRequest;
35 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
36 import org.apache.syncope.common.lib.SyncopeConstants;
37 import org.apache.syncope.common.lib.info.JavaImplInfo;
38 import org.apache.syncope.common.lib.info.NumbersInfo;
39 import org.apache.syncope.common.lib.info.PlatformInfo;
40 import org.apache.syncope.common.lib.info.SystemInfo;
41 import org.apache.syncope.common.lib.types.EntitlementsHolder;
42 import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
43 import org.apache.syncope.common.lib.types.TaskType;
44 import org.apache.syncope.common.rest.api.RESTHeaders;
45 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
46 import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
47 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
48 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
49 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
50 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
51 import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO;
52 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
53 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
54 import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
55 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
56 import org.apache.syncope.core.persistence.api.dao.UserDAO;
57 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
58 import org.apache.syncope.core.persistence.api.entity.AnyType;
59 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
60 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
61 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
62 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
63 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
64 import org.apache.syncope.core.provisioning.api.ImplementationLookup;
65 import org.apache.syncope.core.spring.security.AuthContextUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68 import org.springframework.beans.factory.annotation.Autowired;
69 import org.springframework.boot.actuate.info.Info;
70 import org.springframework.boot.actuate.info.InfoContributor;
71 import org.springframework.context.PayloadApplicationEvent;
72 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
73 import org.springframework.transaction.annotation.Transactional;
74
75 public class DefaultSyncopeCoreInfoContributor implements SyncopeCoreInfoContributor, InfoContributor {
76
77 protected static final Logger LOG = LoggerFactory.getLogger(DefaultSyncopeCoreInfoContributor.class);
78
79 protected static final Object MONITOR = new Object();
80
81 protected static PlatformInfo PLATFORM_INFO;
82
83 protected static SystemInfo SYSTEM_INFO;
84
85 protected static final Pattern THREADPOOLTASKEXECUTOR_PATTERN = Pattern.compile(
86 ".*, pool size = ([0-9]+), "
87 + "active threads = ([0-9]+), "
88 + "queued tasks = ([0-9]+), "
89 + "completed tasks = ([0-9]+).*");
90
91 protected static NumbersInfo.TaskExecutorInfo getTaskExecutorInfo(final String toString) {
92 NumbersInfo.TaskExecutorInfo info = new NumbersInfo.TaskExecutorInfo();
93
94 Matcher matcher = THREADPOOLTASKEXECUTOR_PATTERN.matcher(toString);
95 if (matcher.matches() && matcher.groupCount() == 4) {
96 try {
97 info.setSize(Integer.parseInt(matcher.group(1)));
98 } catch (NumberFormatException e) {
99 LOG.error("While parsing thread pool size", e);
100 }
101 try {
102 info.setActive(Integer.parseInt(matcher.group(2)));
103 } catch (NumberFormatException e) {
104 LOG.error("While parsing active threads #", e);
105 }
106 try {
107 info.setQueued(Integer.parseInt(matcher.group(3)));
108 } catch (NumberFormatException e) {
109 LOG.error("While parsing queued threads #", e);
110 }
111 try {
112 info.setCompleted(Integer.parseInt(matcher.group(4)));
113 } catch (NumberFormatException e) {
114 LOG.error("While parsing completed threads #", e);
115 }
116 }
117
118 return info;
119 }
120
121 protected static void initSystemInfo() {
122 if (SYSTEM_INFO == null) {
123 OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
124 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
125
126 SYSTEM_INFO = new SystemInfo();
127 try {
128 SYSTEM_INFO.setHostname(InetAddress.getLocalHost().getHostName());
129 } catch (UnknownHostException e) {
130 LOG.error("Could not get host name", e);
131 }
132
133 SYSTEM_INFO.setOs(operatingSystemMXBean.getName()
134 + ' ' + operatingSystemMXBean.getVersion()
135 + ' ' + operatingSystemMXBean.getArch());
136 SYSTEM_INFO.setAvailableProcessors(operatingSystemMXBean.getAvailableProcessors());
137 SYSTEM_INFO.setJvm(
138 runtimeMXBean.getVmName()
139 + ' ' + System.getProperty("java.version")
140 + ' ' + runtimeMXBean.getVmVendor());
141 SYSTEM_INFO.setStartTime(runtimeMXBean.getStartTime());
142 }
143 }
144
145 @Autowired
146 protected HttpServletRequest request;
147
148 protected final AnyTypeDAO anyTypeDAO;
149
150 protected final AnyTypeClassDAO anyTypeClassDAO;
151
152 protected final ExternalResourceDAO resourceDAO;
153
154 protected final UserDAO userDAO;
155
156 protected final GroupDAO groupDAO;
157
158 protected final AnyObjectDAO anyObjectDAO;
159
160 protected final RoleDAO roleDAO;
161
162 protected final PolicyDAO policyDAO;
163
164 protected final TaskDAO taskDAO;
165
166 protected final VirSchemaDAO virSchemaDAO;
167
168 protected final SecurityQuestionDAO securityQuestionDAO;
169
170 protected final NotificationDAO notificationDAO;
171
172 protected final ConfParamOps confParamOps;
173
174 protected final ConnIdBundleManager bundleManager;
175
176 protected final ImplementationLookup implLookup;
177
178 protected final Map<String, ThreadPoolTaskExecutor> taskExecutors;
179
180 protected final PersistenceInfoDAO persistenceInfoDAO;
181
182 public DefaultSyncopeCoreInfoContributor(
183 final AnyTypeDAO anyTypeDAO,
184 final AnyTypeClassDAO anyTypeClassDAO,
185 final ExternalResourceDAO resourceDAO,
186 final UserDAO userDAO,
187 final GroupDAO groupDAO,
188 final AnyObjectDAO anyObjectDAO,
189 final RoleDAO roleDAO,
190 final PolicyDAO policyDAO,
191 final NotificationDAO notificationDAO,
192 final TaskDAO taskDAO,
193 final VirSchemaDAO virSchemaDAO,
194 final SecurityQuestionDAO securityQuestionDAO,
195 final ConfParamOps confParamOps,
196 final ConnIdBundleManager bundleManager,
197 final ImplementationLookup implLookup,
198 final Map<String, ThreadPoolTaskExecutor> taskExecutors,
199 final PersistenceInfoDAO persistenceInfoDAO) {
200
201 this.anyTypeDAO = anyTypeDAO;
202 this.anyTypeClassDAO = anyTypeClassDAO;
203 this.resourceDAO = resourceDAO;
204 this.userDAO = userDAO;
205 this.groupDAO = groupDAO;
206 this.anyObjectDAO = anyObjectDAO;
207 this.roleDAO = roleDAO;
208 this.policyDAO = policyDAO;
209 this.notificationDAO = notificationDAO;
210 this.taskDAO = taskDAO;
211 this.virSchemaDAO = virSchemaDAO;
212 this.securityQuestionDAO = securityQuestionDAO;
213 this.confParamOps = confParamOps;
214 this.bundleManager = bundleManager;
215 this.implLookup = implLookup;
216 this.taskExecutors = taskExecutors;
217 this.persistenceInfoDAO = persistenceInfoDAO;
218 }
219
220 protected boolean isSelfRegAllowed() {
221 return confParamOps.get(AuthContextUtils.getDomain(), "selfRegistration.allowed", false, Boolean.class);
222 }
223
224 protected boolean isPwdResetAllowed() {
225 return confParamOps.get(AuthContextUtils.getDomain(), "passwordReset.allowed", false, Boolean.class);
226 }
227
228 protected boolean isPwdResetRequiringSecurityQuestions() {
229 return confParamOps.get(AuthContextUtils.getDomain(), "passwordReset.securityQuestion", true, Boolean.class);
230 }
231
232 protected void buildPlatform() {
233 synchronized (this) {
234 if (PLATFORM_INFO == null) {
235 PLATFORM_INFO = new PlatformInfo();
236
237 PLATFORM_INFO.getConnIdLocations().addAll(bundleManager.getLocations().stream().
238 map(URI::toASCIIString).collect(Collectors.toList()));
239
240 ImplementationTypesHolder.getInstance().getValues().forEach((typeName, typeInterface) -> {
241 Set<String> classNames = implLookup.getClassNames(typeName);
242 if (classNames != null) {
243 JavaImplInfo javaImplInfo = new JavaImplInfo();
244 javaImplInfo.setType(typeName);
245 javaImplInfo.getClasses().addAll(classNames);
246
247 PLATFORM_INFO.getJavaImplInfos().add(javaImplInfo);
248 }
249 });
250 }
251
252 PLATFORM_INFO.setSelfRegAllowed(isSelfRegAllowed());
253 PLATFORM_INFO.setPwdResetAllowed(isPwdResetAllowed());
254 PLATFORM_INFO.setPwdResetRequiringSecurityQuestions(isPwdResetRequiringSecurityQuestions());
255
256 PLATFORM_INFO.getEntitlements().clear();
257 PLATFORM_INFO.getEntitlements().addAll(EntitlementsHolder.getInstance().getValues());
258
259 PLATFORM_INFO.getImplementationTypes().clear();
260 PLATFORM_INFO.getImplementationTypes().addAll(ImplementationTypesHolder.getInstance().getValues().keySet());
261
262 AuthContextUtils.callAsAdmin(AuthContextUtils.getDomain(), () -> {
263 PLATFORM_INFO.getAnyTypes().clear();
264 PLATFORM_INFO.getAnyTypes().addAll(anyTypeDAO.findAll().stream().
265 map(AnyType::getKey).collect(Collectors.toList()));
266
267 PLATFORM_INFO.getUserClasses().clear();
268 PLATFORM_INFO.getUserClasses().addAll(anyTypeDAO.findUser().getClasses().stream().
269 map(AnyTypeClass::getKey).collect(Collectors.toList()));
270
271 PLATFORM_INFO.getAnyTypeClasses().clear();
272 PLATFORM_INFO.getAnyTypeClasses().addAll(anyTypeClassDAO.findAll().stream().
273 map(AnyTypeClass::getKey).collect(Collectors.toList()));
274
275 PLATFORM_INFO.getResources().clear();
276 PLATFORM_INFO.getResources().addAll(resourceDAO.findAll().stream().
277 map(ExternalResource::getKey).collect(Collectors.toList()));
278 return null;
279 });
280 }
281 }
282
283 protected NumbersInfo buildNumbers(final String domain) {
284 return AuthContextUtils.callAsAdmin(domain, () -> {
285 NumbersInfo numbersInfo = new NumbersInfo();
286
287 numbersInfo.setTotalUsers(userDAO.count());
288 numbersInfo.getUsersByRealm().putAll(userDAO.countByRealm());
289 numbersInfo.getUsersByStatus().putAll(userDAO.countByStatus());
290
291 numbersInfo.setTotalGroups(groupDAO.count());
292 numbersInfo.getGroupsByRealm().putAll(groupDAO.countByRealm());
293
294 Map<AnyType, Integer> anyObjectNumbers = anyObjectDAO.countByType();
295 int i = 0;
296 for (Iterator<Map.Entry<AnyType, Integer>> itor = anyObjectNumbers.entrySet().iterator();
297 i < 2 && itor.hasNext(); i++) {
298
299 Map.Entry<AnyType, Integer> entry = itor.next();
300 if (i == 0) {
301 numbersInfo.setAnyType1(entry.getKey().getKey());
302 numbersInfo.setTotalAny1(entry.getValue());
303 numbersInfo.getAny1ByRealm().putAll(anyObjectDAO.countByRealm(entry.getKey()));
304 } else {
305 numbersInfo.setAnyType2(entry.getKey().getKey());
306 numbersInfo.setTotalAny2(entry.getValue());
307 numbersInfo.getAny2ByRealm().putAll(anyObjectDAO.countByRealm(entry.getKey()));
308 }
309 }
310
311 numbersInfo.setTotalResources(resourceDAO.count());
312
313 numbersInfo.setTotalRoles(roleDAO.count());
314
315 numbersInfo.getConfCompleteness().put(
316 NumbersInfo.ConfItem.RESOURCE.name(), numbersInfo.getTotalResources() > 0);
317 numbersInfo.getConfCompleteness().put(
318 NumbersInfo.ConfItem.ACCOUNT_POLICY.name(), !policyDAO.find(AccountPolicy.class).isEmpty());
319 numbersInfo.getConfCompleteness().put(
320 NumbersInfo.ConfItem.PASSWORD_POLICY.name(), !policyDAO.find(PasswordPolicy.class).isEmpty());
321 numbersInfo.getConfCompleteness().put(
322 NumbersInfo.ConfItem.NOTIFICATION.name(), !notificationDAO.findAll().isEmpty());
323 numbersInfo.getConfCompleteness().put(
324 NumbersInfo.ConfItem.PULL_TASK.name(), !taskDAO.findAll(TaskType.PULL).isEmpty());
325 numbersInfo.getConfCompleteness().put(
326 NumbersInfo.ConfItem.VIR_SCHEMA.name(), !virSchemaDAO.findAll().isEmpty());
327 numbersInfo.getConfCompleteness().put(
328 NumbersInfo.ConfItem.ANY_TYPE.name(), !anyObjectNumbers.isEmpty());
329 numbersInfo.getConfCompleteness().put(
330 NumbersInfo.ConfItem.SECURITY_QUESTION.name(), !securityQuestionDAO.findAll().isEmpty());
331 numbersInfo.getConfCompleteness().put(
332 NumbersInfo.ConfItem.ROLE.name(), numbersInfo.getTotalRoles() > 0);
333
334 taskExecutors.forEach((name, bean) -> numbersInfo.getTaskExecutorInfos().
335 put(name, getTaskExecutorInfo(bean.getThreadPoolExecutor().toString())));
336
337 return numbersInfo;
338 });
339 }
340
341 protected void buildSystem() {
342 synchronized (MONITOR) {
343 initSystemInfo();
344 }
345 }
346
347 @Transactional(readOnly = true)
348 @Override
349 public void contribute(final Info.Builder builder) {
350 buildPlatform();
351 builder.withDetail("platform", PLATFORM_INFO);
352
353 builder.withDetail("persistence", persistenceInfoDAO.info());
354
355 builder.withDetail(
356 "numbers",
357 buildNumbers(Optional.ofNullable(request.getHeader(RESTHeaders.DOMAIN)).
358 orElse(SyncopeConstants.MASTER_DOMAIN)));
359
360 buildSystem();
361 builder.withDetail("system", SYSTEM_INFO);
362 }
363
364 @Override
365 public void addLoadInstant(final PayloadApplicationEvent<SystemInfo.LoadInstant> event) {
366 synchronized (MONITOR) {
367 initSystemInfo();
368 SYSTEM_INFO.getLoad().add(event.getPayload());
369 }
370 }
371 }