diff --git a/src/main/java/org/alfresco/heartbeat/HBDataCollectorServiceImpl.java b/src/main/java/org/alfresco/heartbeat/HBDataCollectorServiceImpl.java index d2612d0cc5..2c2f934077 100644 --- a/src/main/java/org/alfresco/heartbeat/HBDataCollectorServiceImpl.java +++ b/src/main/java/org/alfresco/heartbeat/HBDataCollectorServiceImpl.java @@ -86,13 +86,13 @@ public class HBDataCollectorServiceImpl implements HBDataCollectorService } @Override - public boolean isHbEnabled() + public synchronized boolean isHbEnabled() { return enabled; } @Override - public void setHbEnabled(boolean enabled) + public synchronized void setHbEnabled(boolean enabled) { this.enabled = enabled; } diff --git a/src/main/java/org/alfresco/heartbeat/HeartBeat.java b/src/main/java/org/alfresco/heartbeat/HeartBeat.java index 673c9c1bb1..891b8bbf32 100644 --- a/src/main/java/org/alfresco/heartbeat/HeartBeat.java +++ b/src/main/java/org/alfresco/heartbeat/HeartBeat.java @@ -130,27 +130,6 @@ public class HeartBeat implements LicenseChangeHandler } } -// private synchronized void setHeartBeatUrl(String heartBeatUrl) -// { -// this.heartBeatUrl = heartBeatUrl; -// } -// -// // Determine the URL to send the heartbeat to from the license if not set -// private synchronized String getHeartBeatUrl() -// { -// if (heartBeatUrl == null) -// { -// // GC: Ignore the standard heartbeat URL and always use the AWS/Lambda URL -//// LicenseDescriptor licenseDescriptor = licenseService.getLicense(); -//// String url = (licenseDescriptor == null) ? null : licenseDescriptor.getHeartBeatUrl(); -//// setHeartBeatUrl(url == null ? HeartBeat.DEFAULT_URL : url); -// setHeartBeatUrl(LAMBDA_INGEST_URL); -// } -// -// logger.debug("Returning heartBeatUrl: " + heartBeatUrl); -// -// return heartBeatUrl; -// } /** * @return true if the heartbeat is currently enabled @@ -182,7 +161,6 @@ public class HeartBeat implements LicenseChangeHandler { logger.debug("Update license called"); - //setHeartBeatUrl(licenseDescriptor.getHeartBeatUrl()); boolean newEnabled = !licenseDescriptor.isHeartBeatDisabled(); if (newEnabled != dataCollectorService.isHbEnabled()) diff --git a/src/main/java/org/alfresco/heartbeat/RepositoryDataCollector.java b/src/main/java/org/alfresco/heartbeat/RepositoryDataCollector.java new file mode 100644 index 0000000000..0fc06cf75b --- /dev/null +++ b/src/main/java/org/alfresco/heartbeat/RepositoryDataCollector.java @@ -0,0 +1,284 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.heartbeat; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.security.GeneralSecurityException; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.heartbeat.datasender.HBData; +import org.alfresco.repo.descriptor.DescriptorDAO; +import org.alfresco.repo.dictionary.CustomModelsInfo; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.usage.RepoUsageComponent; +import org.alfresco.service.cmr.admin.RepoUsage; +import org.alfresco.service.cmr.dictionary.CustomModelService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.license.LicenseException; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.Base64; + +public class RepositoryDataCollector extends HBBaseDataCollector +{ + + /** The logger. */ + private static final Log logger = LogFactory.getLog(RepositoryDataCollector.class); + + /** + * The parameters that we expect to remain static throughout the lifetime of the repository. There is no need to + * continuously update these. + */ + private Map staticParameters; + + /** The transaction service. */ + private TransactionService transactionService; + + /** DAO for current repository descriptor. */ + private DescriptorDAO currentRepoDescriptorDAO; + + /** DAO for current descriptor. */ + private DescriptorDAO serverDescriptorDAO; + + /** The authority service. */ + private AuthorityService authorityService; + + private RepoUsageComponent repoUsageComponent; + + /** Provides information about custom models */ + private CustomModelService customModelService; + + public void setCurrentRepoDescriptorDAO(DescriptorDAO currentRepoDescriptorDAO) + { + this.currentRepoDescriptorDAO = currentRepoDescriptorDAO; + } + + public void setServerDescriptorDAO(DescriptorDAO serverDescriptorDAO) + { + this.serverDescriptorDAO = serverDescriptorDAO; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setRepoUsageComponent(RepoUsageComponent repoUsageComponent) + { + this.repoUsageComponent = repoUsageComponent; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setCustomModelService(CustomModelService customModelService) + { + this.customModelService = customModelService; + } + + @Override + public List collectData() + { + List collectedData = new LinkedList<>(); + + RetryingTransactionHelper.RetryingTransactionCallback initCallback = new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + lazyInit(); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(initCallback, true); + + // collect repository info data + logger.debug("Preparing repository info data..."); + Map infoValues = new HashMap(); + infoValues.put("repoName", this.staticParameters.get("repoName")); + infoValues.put("edition", this.staticParameters.get("edition")); + infoValues.put("versionMajor", this.staticParameters.get("versionMajor")); + infoValues.put("versionMinor", this.staticParameters.get("versionMinor")); + infoValues.put("schema", this.staticParameters.get("schema")); + HBData infoData = new HBData( + this.currentRepoDescriptorDAO.getDescriptor().getId(), + "acs.repository.info", + "1.0", + new Date(), + infoValues); + collectedData.add(infoData); + + // collect repository usage (system) data + logger.debug("Preparing repository usage (system) data..."); + Runtime runtime = Runtime.getRuntime(); + Map systemUsageValues = new HashMap(); + systemUsageValues.put("memFree", runtime.freeMemory()); + systemUsageValues.put("memMax", runtime.maxMemory()); + systemUsageValues.put("memTotal", runtime.totalMemory()); + HBData systemUsageData = new HBData( + this.currentRepoDescriptorDAO.getDescriptor().getId(), + "acs.repository.usage.system", + "1.0", + new Date(), + systemUsageValues); + collectedData.add(systemUsageData); + + // collect repository usage (model) data + logger.debug("Preparing repository usage (model) data..."); + final CustomModelsInfo customModelsInfo = transactionService.getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionHelper.RetryingTransactionCallback() + { + public CustomModelsInfo execute() + { + return customModelService.getCustomModelsInfo(); + } + }, true); + + Map modelUsageValues = new HashMap(); + modelUsageValues.put("numOfActiveModels", new Integer(customModelsInfo.getNumberOfActiveModels())); + modelUsageValues.put("numOfActiveTypes", new Integer(customModelsInfo.getNumberOfActiveTypes())); + modelUsageValues.put("numOfActiveAspects", new Integer(customModelsInfo.getNumberOfActiveAspects())); + HBData modelUsageData = new HBData( + this.currentRepoDescriptorDAO.getDescriptor().getId(), + "acs.repository.usage.model", + "1.0", + new Date(), + modelUsageValues); + collectedData.add(modelUsageData); + + return collectedData; + } + + /** + * Initializes static parameters on first invocation. Avoid doing it on construction due to bootstrap dependencies + * (e.g. patch service must have run) + * + * @throws GeneralSecurityException + * @throws IOException + */ + private synchronized void lazyInit() throws GeneralSecurityException, IOException + { + if (this.staticParameters == null) + { + this.staticParameters = new TreeMap(); + + // Load up the static parameters + final String ip = getLocalIps(); + this.staticParameters.put("ip", ip); + final String uid; + final Descriptor currentRepoDescriptor = this.currentRepoDescriptorDAO.getDescriptor(); + if (currentRepoDescriptor != null) + { + uid = currentRepoDescriptor.getId(); + this.staticParameters.put("uid", uid); + } + else + { + uid = "Unknown"; + } + + final Descriptor serverDescriptor = this.serverDescriptorDAO.getDescriptor(); + this.staticParameters.put("repoName", serverDescriptor.getName()); + this.staticParameters.put("edition", serverDescriptor.getEdition()); + this.staticParameters.put("versionMajor", serverDescriptor.getVersionMajor()); + this.staticParameters.put("versionMinor", serverDescriptor.getVersionMinor()); + this.staticParameters.put("schema", new Integer(serverDescriptor.getSchema())); + this.staticParameters.put("numUsers", new Integer(this.authorityService.getAllAuthoritiesInZone( + AuthorityService.ZONE_APP_DEFAULT, AuthorityType.USER).size())); + this.staticParameters.put("numGroups", new Integer(this.authorityService.getAllAuthoritiesInZone( + AuthorityService.ZONE_APP_DEFAULT, AuthorityType.GROUP).size())); + + if(repoUsageComponent != null) + { + RepoUsage usage = repoUsageComponent.getUsage(); + + if (usage.getUsers() != null) + { + this.staticParameters.put("licenseUsers", new Long((usage.getUsers()))); + } + } + + } + } + + /** + * Attempts to get all the local IP addresses of this machine in order to distinguish it from other nodes in the + * same network. The machine may use a static IP address in conjunction with a loopback adapter (e.g. to support + * Oracle on Windows), so the IP of the default network interface may not be enough to uniquely identify this + * machine. + * + * @return the local IP addresses, separated by the '/' character + */ + private String getLocalIps() + { + final StringBuilder ip = new StringBuilder(1024); + boolean first = true; + try + { + final Enumeration i = NetworkInterface.getNetworkInterfaces(); + while (i.hasMoreElements()) + { + final NetworkInterface n = i.nextElement(); + final Enumeration j = n.getInetAddresses(); + while (j.hasMoreElements()) + { + InetAddress a = j.nextElement(); + if (a.isLoopbackAddress()) + { + continue; + } + if (first) + { + first = false; + } + else + { + ip.append('/'); + } + ip.append(a.getHostAddress()); + } + } + } + catch (final Exception e) + { + // Ignore + } + return first ? "127.0.0.1" : ip.toString(); + } +} diff --git a/src/main/resources/alfresco/heartbeat-context.xml b/src/main/resources/alfresco/heartbeat-context.xml index 69e22e034f..e106777a33 100644 --- a/src/main/resources/alfresco/heartbeat-context.xml +++ b/src/main/resources/alfresco/heartbeat-context.xml @@ -18,8 +18,8 @@ - - + +