Skip to content

Commit

Permalink
work on #3: Spring initialization and usage for configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKunz committed Dec 13, 2018
1 parent b745464 commit ca9fbfb
Show file tree
Hide file tree
Showing 16 changed files with 468 additions and 10 deletions.
3 changes: 2 additions & 1 deletion CONTRIBUTING.MD
Expand Up @@ -2,4 +2,5 @@

##IDE
We recommend using [IntelliJ](https://www.jetbrains.com/idea/download/#section=windows) as IDE for contributing. We also recommend installing the Gradle Plugin and the [Save Actions Plugin](https://plugins.jetbrains.com/plugin/7642-save-actions).
After installing IntelliJ you can just import the root folder of this repository as a Gradle project. When using the Save Actions Plugin you in addition should to copy the *saveactions_settings.xml* from the *config/ide/intellij/* directory to the .idea folder which is generated on project import.
After installing IntelliJ you can just import the root folder of this repository as a Gradle project. When using the Save Actions Plugin you in addition should to copy the *saveactions_settings.xml* from the *config/ide/intellij/* directory to the .idea folder which is generated on project import.
As we use [Lombok](https://projectlombok.org/) we also recommend installing and enabling the [Lombok Plugin](https://plugins.jetbrains.com/plugin/6317-lombok-plugin) for IntelliJ.
3 changes: 3 additions & 0 deletions build.gradle
Expand Up @@ -18,6 +18,8 @@ subprojects {
buildTools(
'jarcheck:jarcheck:1.5'
)
compileOnly 'org.projectlombok:lombok:1.18.4'
annotationProcessor 'org.projectlombok:lombok:1.18.4'
}

compileJava {
Expand All @@ -28,6 +30,7 @@ subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}


// use jarCheck to make sure all classes in our dependencies are at maximum in version 1.8
task checkDependencyJavaVersions {
Expand Down
Expand Up @@ -33,7 +33,7 @@ public static void agentmain(String agentArgs, Instrumentation inst) {
public static void premain(String agentArgs, Instrumentation inst) {
try {
ClassLoader icl = initializeClasspath(inst);
AgentManager.startOrReplaceInspectitCore(icl);
AgentManager.startOrReplaceInspectitCore(icl, agentArgs, inst);
} catch (Exception e) {
e.printStackTrace();
}
Expand Down
@@ -1,5 +1,7 @@
package rocks.inspectit.oce.bootstrap;

import java.lang.instrument.Instrumentation;

/**
* Manages the running Agent. This class is responsible for starting and stopping {@link rocks.inspectit.oce.core.AgentImpl}
*
Expand All @@ -9,15 +11,22 @@ public class AgentManager {

public static IAgent agentInstance = null;

public static synchronized void startOrReplaceInspectitCore(ClassLoader inspectITClassLoader) {
/**
* If an Agent is already running, invoking this method first stops it.
* Afterwards it tries to start a new Agent from the given Classpath.
* @param inspectITClassLoader the classloader of inspectit-core
* @param agentCmdArgs the command line arguments to pass to the Agent
* @param instrumentation the {@link Instrumentation} to pass to the Agent
*/
public static synchronized void startOrReplaceInspectitCore(ClassLoader inspectITClassLoader, String agentCmdArgs, Instrumentation instrumentation) {
if (agentInstance != null) {
agentInstance.destroy();
agentInstance = null;
}
try {
Class<?> implClass = Class.forName("rocks.inspectit.oce.core.AgentImpl", true, inspectITClassLoader);
agentInstance = (IAgent) implClass.newInstance();
agentInstance.start();
agentInstance.start(agentCmdArgs, instrumentation);
} catch (Exception e) {
e.printStackTrace();
}
Expand Down
@@ -1,16 +1,20 @@
package rocks.inspectit.oce.bootstrap;

import java.lang.instrument.Instrumentation;

/**
* Controller itnerface for the Agent. Its implementation is {@link rocks.inspectit.oce.core.AgentImpl}.
* The implementation must provide a default cosntructor without side effects!
* The actual initialization should happen in {@link #start()}, which is called by {@link AgentManager}
* The actual initialization should happen in {@link #start(String, Instrumentation)}, which is called by {@link AgentManager}
*/
public interface IAgent {

/**
* Initialized and starts the agent.
* @param agentCmdArgs the command line arguments passed to the Agent
* @param instrumentation the {@link Instrumentation} instance passed to the agent
*/
void start();
void start(String agentCmdArgs, Instrumentation instrumentation);

/**
* Shuts down and destroys the agent.
Expand Down
4 changes: 3 additions & 1 deletion inspectit-oce-core/build.gradle
Expand Up @@ -32,7 +32,9 @@ dependencies {
)

implementation(
'org.slf4j:slf4j-api:1.7.25'
'org.springframework.boot:spring-boot:2.1.1.RELEASE',
'org.yaml:snakeyaml:1.23',
'ch.qos.logback:logback-classic:1.2.3'
)
}

Expand Down
@@ -1,16 +1,45 @@
package rocks.inspectit.oce.core;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import rocks.inspectit.oce.bootstrap.IAgent;
import rocks.inspectit.oce.core.config.PropertySourcesInitializer;
import rocks.inspectit.oce.core.config.SpringConfiguration;

import java.lang.instrument.Instrumentation;

/**
* Implementation for the {@link IAgent} interface.
* This clas sis responsible forsetting up the spring context for inspectIT.
*
* @author Jonas Kunz
*/
@Slf4j
public class AgentImpl implements IAgent {

private AnnotationConfigApplicationContext ctx;

@Override
public void start() {
System.out.println("Starting Agent");
public void start(String cmdArgs, Instrumentation instrumentation) {

log.info("Starting inspectIT OCE Agent...");
ctx = new AnnotationConfigApplicationContext();
ctx.setClassLoader(AgentImpl.class.getClassLoader());
ctx.registerShutdownHook();

//Allows to use autowiring to acquire the Instrumentation instance
ctx.getBeanFactory().registerSingleton("instrumentation", instrumentation);

PropertySourcesInitializer.configurePropertySources(ctx);

ctx.register(SpringConfiguration.class);
ctx.refresh();
}


@Override
public void destroy() {
System.out.println("Shutting down Agent");
log.info("Shutting down inspectIT OCE Agent");
ctx.close();
}
}
@@ -0,0 +1,37 @@
package rocks.inspectit.oce.core.config;


import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
import rocks.inspectit.oce.core.config.model.InspectitConfig;

import javax.annotation.PostConstruct;

/**
* Compoennt responsible for loading and reloading the inspectit configurations.
* The configuration is read from the properties of the spring environment
*
* @author Jonas Kunz
*/
@Component
public class ConfigurationCenter {

@Autowired
ConfigurableEnvironment env;

@Getter
private InspectitConfig currentConfiguration;

/**
* (Re-)loads the {@link InspectitConfig} from the environemnt.
* If any changes are detected an event is generated.
*/
@PostConstruct
public void reloadConfiguration() {
currentConfiguration = InspectitConfig.createFromEnvironment(env);
System.out.println(currentConfiguration);
//TODO: compare with previous config: if any changes are present send an event
}
}
@@ -0,0 +1,79 @@
package rocks.inspectit.oce.core.config;

import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;
import rocks.inspectit.oce.core.config.filebased.DirectoryPropertySource;
import rocks.inspectit.oce.core.config.filebased.PropertyFileUtils;
import rocks.inspectit.oce.core.config.model.InspectitConfig;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.function.BiConsumer;

/**
* This class is responsible for registering all {@link org.springframework.core.env.PropertySource}s required for the initialization of inspectIT.
*
* @author Jonas Kunz
*/
@Slf4j
public class PropertySourcesInitializer {

private static final String DEFAULT_CONFIG_PATH = "/config/default.yml";
private static final String DEFAULT_CONFIG_PROPERTYSOURCE_NAME = "inspectitDefaults";

/**
* Sorted list of all configuration sources.
* They are loaded in the given order. The earlier a configuration appears in this list, the higher its priority.
* This means that configurations loaded from items appearing earlier in the list overwrite configurations from items appearing later in the list.
* In contrast items appearing first in the list can provide information for loading items appearing later in the list.
*/
private static final List<BiConsumer<MutablePropertySources, InspectitConfig>> configurationInitializationSteps = Arrays.asList(
PropertySourcesInitializer::addFileBasedConfiguration
);

/**
* Configures the {@link org.springframework.core.env.PropertySource}s of the given spring context.
*
* @param ctx the spring context
*/
public static void configurePropertySources(AnnotationConfigApplicationContext ctx) {
ConfigurableEnvironment env = ctx.getEnvironment();
MutablePropertySources propsList = env.getPropertySources();
addAgentDefaultYaml(propsList);

for (val initializer : configurationInitializationSteps) {
initializer.accept(env.getPropertySources(), InspectitConfig.createFromEnvironment(env));
}

log.info("Registered Configuration Sources:");
env.getPropertySources().stream().forEach(ps -> log.info(" {}", ps.getName()));
}

private static void addAgentDefaultYaml(MutablePropertySources propsList) {
ClassPathResource defaultYamlResource = new ClassPathResource(DEFAULT_CONFIG_PATH, PropertySourcesInitializer.class.getClassLoader());
Properties defaultProps = PropertyFileUtils.readYamlFiles(defaultYamlResource);
propsList.addLast(new PropertiesPropertySource(DEFAULT_CONFIG_PROPERTYSOURCE_NAME, defaultProps));
}

private static void addFileBasedConfiguration(MutablePropertySources propsList, InspectitConfig currentConfig) {
String path = currentConfig.getConfig().getFileBased().getPath();
Path dirPath = Paths.get(path);
Boolean enabled = currentConfig.getConfig().getFileBased().isEnabled();
boolean fileBasedConfigEnabled = enabled && path != null && !path.isEmpty() && Files.exists(dirPath) && Files.isDirectory(dirPath);
if (fileBasedConfigEnabled) {
log.info("initializing file based configuration from dir: {}", path);
val dps = new DirectoryPropertySource("fileBasedConfig", Paths.get(path));
propsList.addBefore(DEFAULT_CONFIG_PROPERTYSOURCE_NAME, dps);
dps.reload(propsList);
}
}
}
@@ -0,0 +1,10 @@
package rocks.inspectit.oce.core.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("rocks.inspectit")
public class SpringConfiguration {

}

0 comments on commit ca9fbfb

Please sign in to comment.