org.jdiameter.api.StackManager Maven / Gradle / Ivy
/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2016, TeleStax Inc. and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* JBoss, Home of Professional Open Source
* Copyright 2007-2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jdiameter.api;
import java.io.PrintWriter;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* The basic service for managing a set of Diameter stacks.
*
*
* As part of its initialization, the DriverManager
class will
* attempt to load the stacks classes referenced in the "diameter.drivers" system property. This allows a user to
* customize the Diameter Drivers used by their applications. For example in your ~/.hotjava/properties file you might
* specify:
*
*
* diameter.drivers=foo.bah.Stack:wombat.diameter.Stack
*
*
* A program can also explicitly load Diameter stacks at any time. For example, the my.diameter.Stack is loaded with the
* following statement:
*
*
* Class.forName("my.diameter.Stack");
*
*
*
* When the method getSession
is called,
* the StackManager
will attempt to locate a suitable stack from amongst those loaded at initialization and
* those loaded explicitly using the same classloader as the current applet or application.
*
* @author [email protected]
* @version 1.5.1 Final
*/
@SuppressWarnings("all") //3rd party lib
public final class StackManager {
private static final Object logSync = new Object();
private static List stacks = new CopyOnWriteArrayList();
private static PrintWriter logWriter = null;
private static boolean initialized = false;
static void initialize() {
if (initialized) {
return;
}
initialized = true;
loadInitialStacks();
println("Diameter StackManager initialized");
}
private StackManager() {
}
/**
* Retrieves the log writer.
*
* The getLogWriter
and setLogWriter
methods should be used instead of the
* get/setlogStream
methods, which are deprecated.
*
* @return a java.io.PrintWriter
object
*
* @see #setLogWriter
*/
public static PrintWriter getLogWriter() {
synchronized (logSync) {
return logWriter;
}
}
/**
* Sets the logging/tracing PrintWriter
object that is used by the StackManager
and all
* drivers.
*
* There is a minor versioning problem created by the introduction of the method setLogWriter
. The
* method setLogWriter
cannot create a PrintStream
object that will be returned by
* getLogStream
*
* @param out the new logging/tracing PrintStream
object;
* null
to disable logging and tracing
*
* @throws SecurityException if a security manager exists and its
* checkPermission
method denies
* setting the log writer
*/
public static void setLogWriter(java.io.PrintWriter out) {
synchronized (logSync) {
logWriter = out;
}
}
/**
* Attempts to locate a stack. The StackManager
attempts to select an appropriate stack from the set of
* registered Diameter stacks.
*
* @param className class name of stack
*
* @return stack instance
*
* @throws InternalException if a manager has internal error
*/
public static synchronized Stack getStack(String className) throws InternalException {
println(new StringBuilder().append("StackManager.getStack(\"").append(className).append("\")").toString());
if (!initialized) {
initialize();
}
// Gets the classloader of the code that called this method, may be null.
ClassLoader callerCL = ClassLoader.getSystemClassLoader();
// Walk through the loaded stacks attempting to locate someone who understands the given URL.
for (StackInfo di : stacks) {
// If the caller does not have permission to load the stack then skip it.
if (getCallerClass(callerCL, di.stackClassName) != di.stackClass) {
println(new StringBuilder().append(" skipping: ").append(di).toString());
continue;
}
println(new StringBuilder().append(" trying ").append(di).toString());
if (di.stackClassName.equals(className)) {
// Success!
println("geStack returning " + di);
return (di.stack);
}
}
println("getStack: no suitable stack");
throw new InternalException("No suitable stack");
}
/**
* Registers the given stack with the ScoketManager
. A newly-loaded stack class should call the method
* registerStack
to make itself known to the StackManager
.
*
* @param stack the new Diameter Stack that is to be registered with the
* StackManager
*
* @throws InternalException if a manager has internal error
*/
public static synchronized void registerStack(Stack stack) throws InternalException {
if (!initialized) {
initialize();
}
StackInfo stackInfo = new StackInfo();
stackInfo.stack = stack;
stackInfo.stackClass = stack.getClass();
stackInfo.stackClassName = stackInfo.stackClass.getName();
stacks.add(stackInfo);
println(new StringBuilder().append("registerStack: ").append(stackInfo).toString());
}
/**
* Drops a driver from the DiameterManager
's list. Applets can only deregister stacks from their own
* classloaders.
*
* @param stack the Diameter stack to drop
*
* @throws InternalException if a manager has internal error
*/
public static synchronized void deregisterStack(Stack stack) throws InternalException {
// Gets the classloader of the code that called this method, may be null.
ClassLoader callerCL = ClassLoader.getSystemClassLoader();
println(new StringBuilder().append("StackManager.deregisterStack: ").append(stack).toString());
// Walk through the loaded stacks.
int i;
StackInfo stackInfo = null;
for (i = 0; i < stacks.size(); i++) {
stackInfo = stacks.get(i);
if (stackInfo.stack == stack) {
break;
}
}
// If we can't find the stack just return.
if (i >= stacks.size()) {
println(" couldn't find stack to unload");
return;
}
// If the caller does not have permission to load the stack then throw a security exception.
if (stackInfo == null || getCallerClass(callerCL, stackInfo.stackClassName) != stackInfo.stackClass) {
throw new SecurityException();
}
// Remove the stack. Other entries in stacks get shuffled down.
stacks.remove(i);
}
/**
* Retrieves an Enumeration with all of the currently loaded Diameter stacks to which the current caller has
* access.
*
*
* Note: The classname of a stack can be found using
* d.getClass().getName()
*
* @return the list of Diameter stacks loaded by the caller's class loader
*/
public static synchronized Enumeration getStacks() {
List result = new CopyOnWriteArrayList();
if (!initialized) {
initialize();
}
// Gets the classloader of the code that called this method, may be null.
ClassLoader callerCL = ClassLoader.getSystemClassLoader();
// Walk through the loaded stacks.
for (StackInfo di : stacks) {
// If the caller does not have permission to load the stack then skip it.
if (getCallerClass(callerCL, di.stackClassName) != di.stackClass) {
println(new StringBuilder().append(" skipping: ").append(di).toString());
continue;
}
result.add(di.stack);
}
return Collections.enumeration(result);
}
public static void println(String message) {
synchronized (logSync) {
if (logWriter != null) {
logWriter.println(message);
// automatic flushing is never enabled, so we must do it ourselves
logWriter.flush();
}
}
}
@SuppressWarnings("all") //3rd party lib
private static Class getCallerClass(ClassLoader callerClassLoader, String stackClassName) {
Class callerC;
try {
callerC = Class.forName(stackClassName, true, callerClassLoader);
} catch (Exception ex) {
callerC = null;
}
return callerC;
}
private static void loadInitialStacks() {
String stacks;
try {
stacks = System.getProperty("diameter.stacks");
} catch (Exception ex) {
stacks = null;
}
println(new StringBuilder().append("StackManager.initialize: diameter.stacks = ").append(stacks).toString());
if (stacks == null) {
return;
}
while (stacks.length() != 0) {
int x = stacks.indexOf(':');
String stack;
if (x < 0) {
stack = stacks;
stacks = "";
} else {
stack = stacks.substring(0, x);
stacks = stacks.substring(x + 1);
}
if (stack.length() == 0) {
continue;
}
try {
println(new StringBuilder().append("StackManager.Initialize: loading ").append(stack).toString());
Class.forName(stack, true, ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println(new StringBuilder().append("StackManager.Initialize: load failed: ").append(ex).toString());
}
}
}
}
class GetPropertyAction implements PrivilegedAction {
private String theProp;
private String defaultVal;
GetPropertyAction(String s) {
theProp = s;
}
GetPropertyAction(String s, String s1) {
theProp = s;
defaultVal = s1;
}
@Override
public String run() {
String s = System.getProperty(theProp);
return s != null ? s : defaultVal;
}
}
@SuppressWarnings("all") //3rd party lib
class StackInfo {
Stack stack;
Class stackClass;
String stackClassName;
@Override
public String toString() {
return (new StringBuilder().append("stack[className=").append(stackClassName).append(",").append(stack).append("]")
.toString());
}
}