strolch/agent/src/main/java/li/strolch/agent/api/StrolchAgent.java

501 lines
17 KiB
Java

/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.agent.api;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import li.strolch.agent.impl.ComponentContainerImpl;
import li.strolch.exception.StrolchException;
import li.strolch.model.Locator;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.RuntimeConfiguration;
import li.strolch.runtime.configuration.StrolchConfiguration;
import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.runtime.privilege.PrivilegedRunnable;
import li.strolch.runtime.privilege.PrivilegedRunnableWithResult;
import li.strolch.utils.ExecutorPool;
import li.strolch.utils.concurrent.ElementLockingHandler;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.helper.SystemHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.*;
import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.runtime.configuration.ConfigurationParser.parseConfiguration;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class StrolchAgent {
public static final String AGENT_VERSION_PROPERTIES = "/agentVersion.properties";
public static final String PROP_TRY_LOCK_TIME_UNIT = "tryLockTimeUnit";
public static final String PROP_TRY_LOCK_TIME = "tryLockTime";
private static final Logger logger = LoggerFactory.getLogger(StrolchAgent.class);
private final StrolchVersion appVersion;
private ComponentContainerImpl container;
private StrolchConfiguration strolchConfiguration;
private ExecutorPool executorPool;
private ElementLockingHandler<Locator> lockHandler;
private JsonObject systemState;
private long systemStateUpdateTime;
private VersionQueryResult versionQueryResult;
public StrolchAgent(StrolchVersion appVersion) {
this.appVersion = appVersion;
}
public StrolchConfiguration getStrolchConfiguration() {
return this.strolchConfiguration;
}
public RuntimeConfiguration getRuntimeConfiguration() {
return this.strolchConfiguration.getRuntimeConfiguration();
}
public ComponentContainer getContainer() {
return this.container;
}
/**
* @see ComponentContainer#hasComponent(Class)
*/
public boolean hasComponent(Class<?> clazz) {
return this.container.hasComponent(clazz);
}
/**
* @see ComponentContainer#getComponent(Class)
*/
public <T> T getComponent(Class<T> clazz) throws IllegalArgumentException {
return this.container.getComponent(clazz);
}
/**
* @see ComponentContainer#getComponentByName(String)
*/
public <T extends StrolchComponent> T getComponentByName(String name) {
return getContainer().getComponentByName(name);
}
/**
* @see ComponentContainer#getComponentsOrderedByRoot()
*/
public List<StrolchComponent> getComponentsOrderedByRoot() {
return this.container.getComponentsOrderedByRoot();
}
public PrivilegeHandler getPrivilegeHandler() throws IllegalArgumentException {
return this.container.getPrivilegeHandler();
}
/**
* @see ComponentContainer#getRealm(String)
*/
public StrolchRealm getRealm(String realm) throws StrolchException {
return getComponent(RealmHandler.class).getRealm(realm);
}
/**
* @see ComponentContainer#getRealm(Certificate)
*/
public StrolchRealm getRealm(Certificate certificate) throws StrolchException {
return this.container.getRealm(certificate);
}
/**
* @see StrolchRealm#openTx(Certificate, Class, boolean)
*/
public StrolchTransaction openTx(Certificate certificate, Class<?> clazz, boolean readOnly) {
return this.container.getRealm(certificate).openTx(certificate, clazz, readOnly);
}
/**
* @see StrolchRealm#openTx(Certificate, String, boolean)
*/
public StrolchTransaction openTx(Certificate certificate, String action, boolean readOnly) {
return this.container.getRealm(certificate).openTx(certificate, action, readOnly);
}
/**
* @see PrivilegeHandler#runAs(String, PrivilegedRunnable)
*/
public void runAs(String systemUser, PrivilegedRunnable runnable) throws Exception {
getPrivilegeHandler().runAs(systemUser, runnable);
}
/**
* @see PrivilegeHandler#runAsAgent(PrivilegedRunnable)
*/
public void runAsAgent(PrivilegedRunnable runnable) throws Exception {
getPrivilegeHandler().runAsAgent(runnable);
}
/**
* @see PrivilegeHandler#runAsAgentWithResult(PrivilegedRunnableWithResult)
*/
public <T> T runAsWithResult(String systemUser, PrivilegedRunnableWithResult<T> runnable) throws Exception {
return getPrivilegeHandler().runAsWithResult(systemUser, runnable);
}
/**
* @see PrivilegeHandler#runAsAgentWithResult(PrivilegedRunnableWithResult)
*/
public <T> T runAsAgentWithResult(PrivilegedRunnableWithResult<T> runnable) throws Exception {
return getPrivilegeHandler().runAsAgentWithResult(runnable);
}
/**
* @return the name of this application as is defined in the configuration
*/
public String getApplicationName() {
return this.strolchConfiguration.getRuntimeConfiguration().getApplicationName();
}
/**
* @return the currently loaded environment of this application as is defined in the configuration
*/
public String getEnvironment() {
return this.strolchConfiguration.getRuntimeConfiguration().getEnvironment();
}
/**
* @return the agent's {@link Locale}
*/
public Locale getLocale() {
return this.strolchConfiguration.getRuntimeConfiguration().getLocale();
}
/**
* @return the agent's time zone
*/
public String getTimezone() {
return this.strolchConfiguration.getRuntimeConfiguration().getTimezone();
}
/**
* Return the {@link ExecutorService} instantiated for this agent
*
* @return the {@link ExecutorService} instantiated for this agent
*/
public ExecutorService getExecutor() {
return getExecutor("Agent");
}
public ExecutorService getExecutor(String poolName) {
return this.executorPool.getExecutor(poolName);
}
/**
* Return the {@link ExecutorService} instantiated for this agent
*
* @return the {@link ExecutorService} instantiated for this agent
*/
public ExecutorService getSingleThreadExecutor() {
return getSingleThreadExecutor("Agent");
}
public ExecutorService getSingleThreadExecutor(String poolName) {
return this.executorPool.getSingleThreadExecutor(poolName);
}
/**
* Return the {@link ScheduledExecutorService} instantiated for this agent
*
* @return the {@link ScheduledExecutorService} instantiated for this agent
*/
public ScheduledExecutorService getScheduledExecutor() {
return getScheduledExecutor("Agent");
}
public ScheduledExecutorService getScheduledExecutor(String poolName) {
return this.executorPool.getScheduledExecutor(poolName);
}
public ElementLockingHandler<Locator> getLockHandler() {
return this.lockHandler;
}
/**
* Initializes the underlying container and prepares the executor services. Before calling this method,
* {@link #setup(String, File, File, File)} must have ben called
*/
public void initialize() {
if (this.container == null)
throw new RuntimeException("Please call setup first!");
this.executorPool = new ExecutorPool();
RuntimeConfiguration configuration = this.strolchConfiguration.getRuntimeConfiguration();
TimeUnit timeUnit = TimeUnit.valueOf(configuration.getString(PROP_TRY_LOCK_TIME_UNIT, TimeUnit.SECONDS.name()));
long time = configuration.getLong(PROP_TRY_LOCK_TIME, 10L);
this.lockHandler = new ElementLockingHandler<>(getScheduledExecutor(), timeUnit, time);
this.container.initialize();
}
/**
* Starts the container
*/
public void start() {
if (this.container == null)
throw new RuntimeException("Please call setup first!");
this.lockHandler.start();
this.container.start();
}
/**
* Stops the container
*/
public void stop() {
if (this.container != null)
this.container.stop();
if (this.lockHandler != null)
this.lockHandler.stop();
}
/**
* Destroys the container and the executor services
*/
public void destroy() {
if (this.executorPool != null)
this.executorPool.destroy();
if (this.container != null)
this.container.destroy();
this.container = null;
}
/**
* <p>
* <b>Note:</b> Use {@link StrolchBootstrapper} instead of calling this method directly!
* </p>
*
* <p>
* Sets up the agent by parsing the configuration file and initializes the given environment
* </p>
*
* @param environment the current environment
* @param configPathF the path to the config directory
* @param dataPathF the path to the data directory
* @param tempPathF the path to the temp directory
*/
void setup(String environment, File configPathF, File dataPathF, File tempPathF) {
String msg = "[{0}] Setting up Strolch Container using the following paths:";
logger.info(MessageFormat.format(msg, environment));
logger.info(" - Config: " + configPathF.getAbsolutePath());
logger.info(" - Data: " + dataPathF.getAbsolutePath());
logger.info(" - Temp: " + tempPathF.getAbsolutePath());
logger.info(" - user.dir: " + SystemHelper.getUserDir());
this.strolchConfiguration = parseConfiguration(environment, configPathF, dataPathF, tempPathF);
ComponentContainerImpl container = new ComponentContainerImpl(this);
container.setup(this.strolchConfiguration);
this.container = container;
RuntimeConfiguration config = this.strolchConfiguration.getRuntimeConfiguration();
logger.info(MessageFormat.format("Setup Agent {0}:{1}", config.getApplicationName(), config.getEnvironment()));
}
protected void assertContainerStarted() {
if (this.container == null || this.container.getState() != ComponentState.STARTED) {
String msg = "Container is not yet started!";
throw new IllegalStateException(msg);
}
}
/**
* @return Returns the pseudo unique Id to be used during object creation from external services.
*/
public static String getUniqueId() {
return StringHelper.getUniqueId();
}
/**
* @return Returns the pseudo unique Id to be used during object creation from external services.
*/
public static Long getUniqueIdLong() {
return StringHelper.getUniqueIdLong();
}
/**
* Returns the version of this agent
*
* @return the version of this agent
*/
public VersionQueryResult getVersion() {
if (this.versionQueryResult == null) {
VersionQueryResult queryResult = new VersionQueryResult();
queryResult.setAppVersion(this.appVersion);
Properties properties = new Properties();
try (InputStream stream = getClass().getResourceAsStream(AGENT_VERSION_PROPERTIES)) {
properties.load(stream);
RuntimeConfiguration runtimeConfiguration = getStrolchConfiguration().getRuntimeConfiguration();
AgentVersion agentVersion = new AgentVersion(runtimeConfiguration.getApplicationName(),
runtimeConfiguration.getEnvironment(), runtimeConfiguration.getLocale(),
runtimeConfiguration.getTimezone(), properties);
queryResult.setAgentVersion(agentVersion);
} catch (IOException e) {
String msg = MessageFormat.format("Failed to read version properties for agent: {0}", e.getMessage());
queryResult.getErrors().add(msg);
logger.error(msg, e);
}
Set<Class<?>> componentTypes = this.container.getComponentTypes();
for (Class<?> componentType : componentTypes) {
StrolchComponent component = (StrolchComponent) this.container.getComponent(componentType);
try {
ComponentVersion componentVersion = component.getVersion();
queryResult.add(componentVersion);
} catch (Exception e) {
String msg = "Failed to read version properties for component {0} due to: {1}";
msg = MessageFormat.format(msg, component.getName(), e.getMessage());
queryResult.getErrors().add(msg);
logger.error(msg, e);
}
}
this.versionQueryResult = queryResult;
}
return this.versionQueryResult;
}
public JsonObject getSystemState(long updateInterval, TimeUnit updateIntervalUnit) {
if (this.systemState == null
|| System.currentTimeMillis() - this.systemStateUpdateTime > updateIntervalUnit.toMillis(
updateInterval)) {
this.systemState = new JsonObject();
JsonObject osJ = new JsonObject();
this.systemState.add(OPERATING_SYSTEM, osJ);
osJ.addProperty(OS_NAME, SystemHelper.osName);
osJ.addProperty(OS_ARCH, SystemHelper.osArch);
osJ.addProperty(OS_VERSION, SystemHelper.osVersion);
osJ.addProperty(JAVA_VENDOR, SystemHelper.javaVendor);
osJ.addProperty(JAVA_VERSION, SystemHelper.javaVersion);
OperatingSystemMXBean osMXBean = ManagementFactory.getOperatingSystemMXBean();
osJ.addProperty(AVAILABLE_PROCESSORS, osMXBean.getAvailableProcessors());
osJ.addProperty(SYSTEM_LOAD_AVERAGE, osMXBean.getSystemLoadAverage());
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
osJ.addProperty(START_TIME, ISO8601.toString(runtimeMXBean.getStartTime()));
osJ.addProperty(UPTIME, runtimeMXBean.getUptime());
// memory
JsonObject memoryJ = new JsonObject();
this.systemState.add(MEMORY, memoryJ);
if (osMXBean instanceof com.sun.management.OperatingSystemMXBean os) {
memoryJ.addProperty(TOTAL_PHYSICAL_MEMORY_SIZE, os.getTotalMemorySize());
memoryJ.addProperty(FREE_PHYSICAL_MEMORY_SIZE, os.getFreeMemorySize());
memoryJ.addProperty(FREE_SWAP_SPACE_SIZE, os.getFreeSwapSpaceSize());
memoryJ.addProperty(COMMITTED_VIRTUAL_MEMORY_SIZE, os.getCommittedVirtualMemorySize());
}
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
memoryJ.addProperty(HEAP_MEMORY_USAGE_INIT, heapMemoryUsage.getInit());
memoryJ.addProperty(HEAP_MEMORY_USAGE_USED, heapMemoryUsage.getUsed());
memoryJ.addProperty(HEAP_MEMORY_USAGE_MAX, heapMemoryUsage.getMax());
memoryJ.addProperty(HEAP_MEMORY_USAGE_COMMITTED, heapMemoryUsage.getCommitted());
// disk space
JsonArray rootsJ = new JsonArray();
this.systemState.add(ROOTS, rootsJ);
File[] roots = File.listRoots();
for (File root : roots) {
JsonObject rootJ = new JsonObject();
rootsJ.add(rootJ);
rootJ.addProperty(PATH, root.getAbsolutePath());
rootJ.addProperty(USABLE_SPACE, root.getUsableSpace());
rootJ.addProperty(USED_SPACE, root.getTotalSpace() - root.getFreeSpace());
rootJ.addProperty(FREE_SPACE, root.getFreeSpace());
rootJ.addProperty(TOTAL_SPACE, root.getTotalSpace());
}
this.systemStateUpdateTime = System.currentTimeMillis();
}
return this.systemState;
}
public void reloadStrolchConfiguration() {
RuntimeConfiguration runtimeConfig = this.strolchConfiguration.getRuntimeConfiguration();
File configPathF = runtimeConfig.getConfigPath();
File dataPathF = runtimeConfig.getDataPath();
File tempPathF = runtimeConfig.getTempPath();
StrolchConfiguration newConfig = parseConfiguration(runtimeConfig.getEnvironment(), configPathF, dataPathF,
tempPathF);
for (String name : this.container.getComponentNames()) {
ComponentConfiguration newComponentConfig = newConfig.getComponentConfiguration(name);
StrolchComponent existingComponent = this.container.getComponentByName(name);
ComponentConfiguration existingComponentConfiguration = existingComponent.getConfiguration();
existingComponentConfiguration.updateProperties(newComponentConfig.getAsMap());
}
RuntimeConfiguration newRuntimeConfiguration = newConfig.getRuntimeConfiguration();
runtimeConfig.updateProperties(newRuntimeConfiguration.getAsMap());
runtimeConfig.setLocale(newRuntimeConfiguration.getLocale());
runtimeConfig.setSupportedLanguages(newRuntimeConfiguration.getSupportedLanguages());
}
public JsonObject toJson() {
JsonObject agentJ = getStrolchConfiguration().getRuntimeConfiguration().toJson();
JsonArray componentsJ = new JsonArray();
List<StrolchComponent> components = getComponentsOrderedByRoot();
for (StrolchComponent component : components) {
componentsJ.add(component.toJson());
}
agentJ.add(COMPONENTS, componentsJ);
return agentJ;
}
}