This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.tomcat.util.modeler.modules;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.modeler.ManagedBean;
import org.apache.tomcat.util.modeler.Registry;
public class MbeansDescriptorsDigesterSource extends ModelerSource
{
private static final Log log =
LogFactory.getLog(MbeansDescriptorsDigesterSource.class);
private static final Object dLock = new Object();
private Registry registry;
private final List<ObjectName> mbeans = new ArrayList<>();
private static Digester digester = null;
private static Digester createDigester() {
Digester digester = new Digester();
digester.setNamespaceAware(false);
digester.setValidating(false);
URL url = Registry.getRegistry(null, null).getClass().getResource
("/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd");
digester.register
("-//Apache Software Foundation//DTD Model MBeans Configuration File",
url.toString());
// Configure the parsing rules
digester.addObjectCreate
("mbeans-descriptors/mbean",
"org.apache.tomcat.util.modeler.ManagedBean");
digester.addSetProperties
("mbeans-descriptors/mbean");
digester.addSetNext
("mbeans-descriptors/mbean",
"add",
"java.lang.Object");
digester.addObjectCreate
("mbeans-descriptors/mbean/attribute",
"org.apache.tomcat.util.modeler.AttributeInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/attribute");
digester.addSetNext
("mbeans-descriptors/mbean/attribute",
"addAttribute",
"org.apache.tomcat.util.modeler.AttributeInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/notification",
"org.apache.tomcat.util.modeler.NotificationInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/notification");
digester.addSetNext
("mbeans-descriptors/mbean/notification",
"addNotification",
"org.apache.tomcat.util.modeler.NotificationInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/notification/descriptor/field",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/notification/descriptor/field");
digester.addSetNext
("mbeans-descriptors/mbean/notification/descriptor/field",
"addField",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addCallMethod
("mbeans-descriptors/mbean/notification/notification-type",
"addNotifType", 0);
digester.addObjectCreate
("mbeans-descriptors/mbean/operation",
"org.apache.tomcat.util.modeler.OperationInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation");
digester.addSetNext
("mbeans-descriptors/mbean/operation",
"addOperation",
"org.apache.tomcat.util.modeler.OperationInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/operation/descriptor/field",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation/descriptor/field");
digester.addSetNext
("mbeans-descriptors/mbean/operation/descriptor/field",
"addField",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/operation/parameter",
"org.apache.tomcat.util.modeler.ParameterInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation/parameter");
digester.addSetNext
("mbeans-descriptors/mbean/operation/parameter",
"addParameter",
"org.apache.tomcat.util.modeler.ParameterInfo");
return digester;
}
public void setRegistry(Registry reg) {
this.registry=reg;
}
public void setSource( Object source ) {
this.source=source;
}
@Override
public List<ObjectName> loadDescriptors( Registry registry, String type,
Object source) throws Exception {
setRegistry(registry);
setSource(source);
execute();
return mbeans;
}
public void execute() throws Exception {
if (registry == null) {
registry = Registry.getRegistry(null, null);
}
InputStream stream = (InputStream) source;
List<ManagedBean> loadedMbeans = new ArrayList<>();
synchronized(dLock) {
if (digester == null) {
digester = createDigester();
}
// Process the input file to configure our registry
try {
// Push our registry object onto the stack
digester.push(loadedMbeans);
digester.parse(stream);
} catch (Exception e) {
log.error(sm.getString("modules.digesterParseError"), e);
throw e;
} finally {
digester.reset();
}
}
for (ManagedBean loadedMbean : loadedMbeans) {
registry.addManagedBean(loadedMbean);
}
}
}

View File

@@ -0,0 +1,392 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.tomcat.util.modeler.modules;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.AttributeInfo;
import org.apache.tomcat.util.modeler.ManagedBean;
import org.apache.tomcat.util.modeler.OperationInfo;
import org.apache.tomcat.util.modeler.ParameterInfo;
import org.apache.tomcat.util.modeler.Registry;
public class MbeansDescriptorsIntrospectionSource extends ModelerSource
{
private static final Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
private Registry registry;
private String type;
private final List<ObjectName> mbeans = new ArrayList<>();
public void setRegistry(Registry reg) {
this.registry=reg;
}
/**
* Used if a single component is loaded
*
* @param type The type
*/
public void setType( String type ) {
this.type=type;
}
public void setSource( Object source ) {
this.source=source;
}
@Override
public List<ObjectName> loadDescriptors(Registry registry, String type,
Object source) throws Exception {
setRegistry(registry);
setType(type);
setSource(source);
execute();
return mbeans;
}
public void execute() throws Exception {
if( registry==null ) registry=Registry.getRegistry(null, null);
try {
ManagedBean managed = createManagedBean(registry, null,
(Class<?>)source, type);
if( managed==null ) return;
managed.setName( type );
registry.addManagedBean(managed);
} catch( Exception ex ) {
log.error(sm.getString("modules.readDescriptorsError"), ex);
}
}
// ------------ Implementation for non-declared introspection classes
private static final Hashtable<String,String> specialMethods = new Hashtable<>();
static {
specialMethods.put( "preDeregister", "");
specialMethods.put( "postDeregister", "");
}
private static final Class<?>[] supportedTypes = new Class[] {
Boolean.class,
Boolean.TYPE,
Byte.class,
Byte.TYPE,
Character.class,
Character.TYPE,
Short.class,
Short.TYPE,
Integer.class,
Integer.TYPE,
Long.class,
Long.TYPE,
Float.class,
Float.TYPE,
Double.class,
Double.TYPE,
String.class,
String[].class,
BigDecimal.class,
BigInteger.class,
ObjectName.class,
Object[].class,
java.io.File.class,
};
/**
* Check if this class is one of the supported types.
* If the class is supported, returns true. Otherwise,
* returns false.
* @param ret The class to check
* @return boolean True if class is supported
*/
private boolean supportedType(Class<?> ret) {
for (int i = 0; i < supportedTypes.length; i++) {
if (ret == supportedTypes[i]) {
return true;
}
}
if (isBeanCompatible(ret)) {
return true;
}
return false;
}
/**
* Check if this class conforms to JavaBeans specifications.
* If the class is conformant, returns true.
*
* @param javaType The class to check
* @return boolean True if the class is compatible.
*/
private boolean isBeanCompatible(Class<?> javaType) {
// Must be a non-primitive and non array
if (javaType.isArray() || javaType.isPrimitive()) {
return false;
}
// Anything in the java or javax package that
// does not have a defined mapping is excluded.
if (javaType.getName().startsWith("java.") ||
javaType.getName().startsWith("javax.")) {
return false;
}
try {
javaType.getConstructor(new Class[]{});
} catch (java.lang.NoSuchMethodException e) {
return false;
}
// Make sure superclass is compatible
Class<?> superClass = javaType.getSuperclass();
if (superClass != null &&
superClass != java.lang.Object.class &&
superClass != java.lang.Exception.class &&
superClass != java.lang.Throwable.class) {
if (!isBeanCompatible(superClass)) {
return false;
}
}
return true;
}
/**
* Process the methods and extract 'attributes', methods, etc.
*
* @param realClass The class to process
* @param methods The methods to process
* @param attMap The attribute map (complete)
* @param getAttMap The readable attributes map
* @param setAttMap The settable attributes map
* @param invokeAttMap The invokable attributes map
*/
private void initMethods(Class<?> realClass, Method methods[], Hashtable<String,Method> attMap,
Hashtable<String,Method> getAttMap, Hashtable<String,Method> setAttMap,
Hashtable<String,Method> invokeAttMap) {
for (int j = 0; j < methods.length; ++j) {
String name = methods[j].getName();
if (Modifier.isStatic(methods[j].getModifiers())) {
continue;
}
if (!Modifier.isPublic(methods[j].getModifiers())) {
if (log.isDebugEnabled()) {
log.debug("Not public " + methods[j] );
}
continue;
}
if (methods[j].getDeclaringClass() == Object.class) {
continue;
}
Class<?> params[] = methods[j].getParameterTypes();
if (name.startsWith("get") && params.length==0) {
Class<?> ret = methods[j].getReturnType();
if (!supportedType(ret)) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j]);
}
continue;
}
name=unCapitalize(name.substring(3));
getAttMap.put(name, methods[j]);
// just a marker, we don't use the value
attMap.put(name, methods[j]);
} else if(name.startsWith("is") && params.length == 0) {
Class<?> ret = methods[j].getReturnType();
if (Boolean.TYPE != ret) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j] + " " + ret );
}
continue;
}
name = unCapitalize(name.substring(2));
getAttMap.put(name, methods[j]);
// just a marker, we don't use the value
attMap.put(name, methods[j]);
} else if (name.startsWith("set") && params.length == 1) {
if (!supportedType(params[0])) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j] + " " + params[0]);
}
continue;
}
name = unCapitalize(name.substring(3));
setAttMap.put(name, methods[j]);
attMap.put(name, methods[j]);
} else {
if (params.length == 0) {
if (specialMethods.get(methods[j].getName()) != null) {
continue;
}
invokeAttMap.put(name, methods[j]);
} else {
boolean supported = true;
for (int i = 0; i < params.length; i++ ) {
if (!supportedType(params[i])) {
supported = false;
break;
}
}
if (supported) {
invokeAttMap.put( name, methods[j]);
}
}
}
}
}
/**
* XXX Find if the 'className' is the name of the MBean or
* the real class ( I suppose first )
* XXX Read (optional) descriptions from a .properties, generated
* from source
* XXX Deal with constructors
*
* @param registry The Bean registry (not used)
* @param domain The bean domain (not used)
* @param realClass The class to analyze
* @param type The bean type
* @return ManagedBean The create MBean
*/
public ManagedBean createManagedBean(Registry registry, String domain,
Class<?> realClass, String type)
{
ManagedBean mbean= new ManagedBean();
Method methods[]=null;
Hashtable<String,Method> attMap = new Hashtable<>();
// key: attribute val: getter method
Hashtable<String,Method> getAttMap = new Hashtable<>();
// key: attribute val: setter method
Hashtable<String,Method> setAttMap = new Hashtable<>();
// key: operation val: invoke method
Hashtable<String,Method> invokeAttMap = new Hashtable<>();
methods = realClass.getMethods();
initMethods(realClass, methods, attMap, getAttMap, setAttMap, invokeAttMap );
try {
Enumeration<String> en = attMap.keys();
while( en.hasMoreElements() ) {
String name = en.nextElement();
AttributeInfo ai=new AttributeInfo();
ai.setName( name );
Method gm = getAttMap.get(name);
if( gm!=null ) {
//ai.setGetMethodObj( gm );
ai.setGetMethod( gm.getName());
Class<?> t=gm.getReturnType();
if( t!=null )
ai.setType( t.getName() );
}
Method sm = setAttMap.get(name);
if( sm!=null ) {
//ai.setSetMethodObj(sm);
Class<?> t = sm.getParameterTypes()[0];
if( t!=null )
ai.setType( t.getName());
ai.setSetMethod( sm.getName());
}
ai.setDescription("Introspected attribute " + name);
if( log.isDebugEnabled()) log.debug("Introspected attribute " +
name + " " + gm + " " + sm);
if( gm==null )
ai.setReadable(false);
if( sm==null )
ai.setWriteable(false);
if( sm!=null || gm!=null )
mbean.addAttribute(ai);
}
// This map is populated by iterating the methods (which end up as
// values in the Map) and obtaining the key from the value. It is
// impossible for a key to be associated with a null value.
for (Entry<String,Method> entry : invokeAttMap.entrySet()) {
String name = entry.getKey();
Method m = entry.getValue();
OperationInfo op=new OperationInfo();
op.setName(name);
op.setReturnType(m.getReturnType().getName());
op.setDescription("Introspected operation " + name);
Class<?> parms[] = m.getParameterTypes();
for(int i=0; i<parms.length; i++ ) {
ParameterInfo pi=new ParameterInfo();
pi.setType(parms[i].getName());
pi.setName(("param" + i).intern());
pi.setDescription(("Introspected parameter param" + i).intern());
op.addParameter(pi);
}
mbean.addOperation(op);
}
if( log.isDebugEnabled())
log.debug("Setting name: " + type );
mbean.setName( type );
return mbean;
} catch( Exception ex ) {
ex.printStackTrace();
return null;
}
}
// -------------------- Utils --------------------
/**
* Converts the first character of the given
* String into lower-case.
*
* @param name The string to convert
* @return String
*/
private static String unCapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.tomcat.util.modeler.modules;
import java.util.List;
import javax.management.ObjectName;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.res.StringManager;
/**
* Source for descriptor data. More sources can be added.
*/
public abstract class ModelerSource {
protected static final StringManager sm = StringManager.getManager(Registry.class);
protected Object source;
/**
* Load data, returns a list of items.
*
* @param registry The registry
* @param type The bean registry type
* @param source Introspected object or some other source
* @return a list of object names
* @throws Exception Error loading descriptors
*/
public abstract List<ObjectName> loadDescriptors(Registry registry,
String type, Object source) throws Exception;
}

View File

@@ -0,0 +1,59 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF 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.
-->
<html>
<head>
<title>org.apache.commons.modeler.modules</title>
</head>
<body>
<p>Implementation classes - should not be used directly. The API is not stable
but eventually the code will be refactored as a collection of mbeans that will be useable
( more or less ) independently.</p>
<p>The MbeanDescriptors* classes are used to extract metadata from different sources. They
are result of few stages of refactoring - now they look very similar with ant tasks and are
close to normal mbeans, with an execute() method. DOM, SER, Introspection and Dynamic mbean
will load metadata from the corresponding sources.
</p>
<p>MbeansSource will load an extended MLET file, similar with jboss. It is not completely
implemented - only modeler mbeans and dynamic mbeans are loaded. The important characteristic
is that all declared mbeans will be registered in the mbean server as model mbeans. For
regular java classes, the description will be used to construct the model mbean. DynamicMbeans
metadata will be converted to model mbean and the model mbean wrapper will be loaded.</p>
<p>The goal of MbeansSource is to implement a simple persistence mechanism. Since all components
are model mbeans, we can detect all changes. The source will be loaded as DOM and modifications
will be made to the tree. The save() method will save the DOM tree - preserving all comments
and having only the changes that are needed.</p>
<p>There are few remaining issues. First, we need to use the persistence metadata to avoid
saving transient fields ( we save an attribute when we detect a change - but we don't know
if this attribute should be saved ). The solution is to use the persistence fields in the
spec - with some reasonable defaults or patterns for introspection or backward compat.
</p>
<p>Another problem is implementing adding and removing components. In catalina, a
factory is used to create the components, and save will operate on all mbeans.
For creation we need to also use a factory - using the "Type" as a parameter. This
will also work very well with Ant1.6 where we can use the component factory to
do a "natural" mapping ( i.e. mbeans can be treated as tasks, with attributes as
task attributes ). The second part can be solve by either using a parameter on
the factory method ( saveTo ? ), or by having a single mbeans source per domain.
</p>
</body>
</html>