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.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 }