co.elastic.apm.agent.sdk.state.GlobalVariables Maven / Gradle / Ivy
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.apm.agent.sdk.state;
import co.elastic.apm.agent.sdk.internal.util.PrivilegedActionUtils;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* As the classes of an instrumentation plugins may be loaded from multiple plugin class loaders,
* there's a need to share global state between those class loaders.
*
* An alternative to this is {@link GlobalState} which can be used to make a whole class scoped globally.
*
*
* Be careful not to store classes from the target class loader or the plugin class loader in a global variable.
* This would otherwise lead to class loader leaks.
* That's because a global variable is referenced from the agent class loader.
* If it held a reference to a class that's loaded by the plugin class loader, the target class loader (such as a webapp class loader)
* is held alive by the following chain of hard references:
* {@code Map of global thread locals (Agent CL) -plugin class instance-> -plugin class-> plugin CL -(parent)-> webapp CL}
*
*/
public class GlobalVariables {
private static final ConcurrentMap registry = new ConcurrentHashMap<>();
/**
* Gets a global variable given an advice class an additional key which identifies the variable within the class.
*
* @param adviceClass the advice class which uses the global variable.
* @param key an additional key which identifies the variable within the class.
* @param defaultValue the default value of the global variable
* @param the type of the variable
* @return a global variable
*/
public static T get(Class> adviceClass, String key, T defaultValue) {
key = adviceClass.getName() + "." + key;
if (PrivilegedActionUtils.getClassLoader(defaultValue.getClass()) != null && !defaultValue.getClass().getName().startsWith("co.elastic.apm.agent")) {
throw new IllegalArgumentException("Registering types specific to an instrumentation plugin would lead to class loader leaks: " + defaultValue);
}
T value = (T) registry.get(key);
if (value == null) {
registry.putIfAbsent(key, defaultValue);
value = (T) registry.get(key);
}
return value;
}
}