<classpathentry kind="lib" path="target/lib/asm.jar"/>
<classpathentry kind="lib" path="target/lib/catalina.jar"/>
<classpathentry kind="lib" path="target/lib/commons-codec.jar"/>
- <classpathentry kind="lib" path="target/lib/coyote.jar"/>
+ <classpathentry kind="lib" path="target/lib/coyote.jar" sourcepath="/home/costin/.m2/repository/org/apache/tomcat/coyote/6.0.20/coyote-6.0.20-sources.jar"/>
<classpathentry kind="lib" path="target/lib/el-api.jar"/>
<classpathentry kind="lib" path="target/lib/jasper-el.jar"/>
<classpathentry kind="lib" path="target/lib/jasper-jdt.jar"/>
<classpathentry kind="lib" path="target/lib/jsp-api.jar"/>
<classpathentry kind="lib" path="target/lib/juli.jar"/>
<classpathentry kind="lib" path="target/lib/junit.jar"/>
- <classpathentry kind="lib" path="target/lib/jzlib.jar"/>
- <classpathentry kind="lib" path="target/lib/servlet-api.jar"/>
+ <classpathentry kind="lib" path="target/lib/jzlib.jar" sourcepath="/home/costin/.m2/repository/com/jcraft/jzlib/1.0.7/jzlib-1.0.7-sources.jar"/>
+ <classpathentry kind="lib" path="target/lib/servlet-api.jar" sourcepath="/tomcat-trunk"/>
<classpathentry kind="output" path="output/classes"/>
</classpath>
*/
package org.apache.tomcat.integration;
+import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DynamicObject {
// Based on MbeansDescriptorsIntrospectionSource
- static Logger log = Logger.getLogger(DynamicObject.class.getName());
+ private static Logger log = Logger.getLogger(DynamicObject.class.getName());
- static Class<?> NO_PARAMS[] = new Class[0];
+ private static Class<?> NO_PARAMS[] = new Class[0];
private static String strArray[] = new String[0];
private Class realClass;
- private Map<String, Method> getAttMap;
+ // Method or Field
+ private Map<String, AccessibleObject> getAttMap;
public DynamicObject(Class beanClass) {
this.realClass = beanClass;
initCache();
}
- public DynamicObject(Class beanClass, boolean noCache) {
- this.realClass = beanClass;
- }
-
private void initCache() {
Method methods[] = null;
- getAttMap = new HashMap<String, Method>();
+ getAttMap = new HashMap<String, AccessibleObject>();
methods = realClass.getMethods();
for (int j = 0; j < methods.length; ++j) {
getAttMap.put(name, methods[j]);
}
}
- }
-
- private boolean ignorable(Method method) {
- if (Modifier.isStatic(method.getModifiers()))
- return true;
- if (!Modifier.isPublic(method.getModifiers())) {
- return true;
- }
- if (method.getDeclaringClass() == Object.class)
- return true;
- return false;
- }
-
- public List<String> attributeNames() {
- List<String> attributes = new ArrayList<String>();
- Method methods[] = realClass.getMethods();
- for (int j = 0; j < methods.length; ++j) {
- String name = methods[j].getName();
- if (ignorable(methods[j])) {
- continue;
- }
- Class<?> params[] = methods[j].getParameterTypes();
- if (name.startsWith("get") && params.length == 0) {
- Class<?> ret = methods[j].getReturnType();
- if (!supportedType(ret)) {
- continue;
- }
- name = unCapitalize(name.substring(3));
- attributes.add(name);
- } else if (name.startsWith("is") && params.length == 0) {
- Class<?> ret = methods[j].getReturnType();
- if (Boolean.TYPE != ret) {
- continue;
- }
- name = unCapitalize(name.substring(2));
- attributes.add(name);
- } else if (name.startsWith("set") && params.length == 1) {
- if (!supportedType(params[0])) {
- continue;
- }
- name = unCapitalize(name.substring(3));
- attributes.add(name);
+ // non-private AtomicInteger and AtomicLong - stats
+ Field fields[] = realClass.getFields();
+ for (int j = 0; j < fields.length; ++j) {
+ if (fields[j].getType() == AtomicInteger.class) {
+ getAttMap.put(fields[j].getName(), fields[j]);
}
}
+
+ }
- return attributes;
+ public List<String> attributeNames() {
+ return new ArrayList<String>(getAttMap.keySet());
}
}
// TODO
- public Object invoke(String method, Object[] params) {
- return null;
- }
-
- public boolean hasHook(String method) {
- return false;
- }
+// public Object invoke(String method, Object[] params) {
+// return null;
+// }
public Object getAttribute(Object o, String att) {
- Method m = getAttMap.get(att);
- if (m == null)
- return null;
- try {
- return m.invoke(o);
- } catch (Throwable e) {
- log.log(Level.INFO, "Error getting attribute " + realClass + " "
- + att, e);
+ AccessibleObject m = getAttMap.get(att);
+ if (m instanceof Method) {
+ try {
+ return ((Method) m).invoke(o);
+ } catch (Throwable e) {
+ log.log(Level.INFO, "Error getting attribute " + realClass + " "
+ + att, e);
+ return null;
+ }
+ } if (m instanceof Field) {
+ if (((Field) m).getType() == AtomicInteger.class) {
+ try {
+ Object value = ((Field) m).get(o);
+ return ((AtomicInteger) value).get();
+ } catch (Throwable e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ } else {
return null;
}
}
+ /**
+ * Set an object-type attribute.
+ *
+ * Use setProperty to use a string value and convert it to the
+ * specific (primitive) type.
+ */
public boolean setAttribute(Object proxy, String name, Object value) {
+ // TODO: use the cache...
String methodName = "set" + capitalize(name);
Method[] methods = proxy.getClass().getMethods();
for (Method m : methods) {
}
public boolean setProperty(Object proxy, String name, String value) {
+ // TODO: use the cache...
String setter = "set" + capitalize(name);
try {
return new String(chars);
}
+ private boolean ignorable(Method method) {
+ if (Modifier.isStatic(method.getModifiers()))
+ return true;
+ if (!Modifier.isPublic(method.getModifiers())) {
+ return true;
+ }
+ if (method.getDeclaringClass() == Object.class)
+ return true;
+ return false;
+ }
+
}
--- /dev/null
+/*
+ * 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.integration.jmx;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.management.ManagementFactory;
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.MBeanInfo;
+import javax.management.MBeanAttributeInfo;
+import javax.management.Attribute;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * This servlet will dump JMX attributes in a simple format
+ * and implement proxy services for modeler.
+ *
+ * @author Costin Manolache
+ */
+public class JMXProxyServlet extends HttpServlet {
+ protected MBeanServer server = null;
+
+ /**
+ * Initialize this servlet.
+ */
+ public void init() throws ServletException {
+ // Retrieve the MBean serverif (server == null) {
+ if( MBeanServerFactory.findMBeanServer(null).size() > 0 ) {
+ server = MBeanServerFactory.findMBeanServer(null).get(0);
+ } else {
+ server = ManagementFactory.getPlatformMBeanServer();
+ }
+ }
+
+
+ /**
+ * Process a GET request for the specified resource.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet-specified error occurs
+ */
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, ServletException
+ {
+
+ response.setContentType("text/plain");
+
+ PrintWriter writer = response.getWriter();
+
+ if( server==null ) {
+ writer.println("Error - No mbean server");
+ return;
+ }
+
+ String qry=request.getParameter("set");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ String val=request.getParameter("val");
+
+ setAttribute( writer, qry, name, val );
+ return;
+ }
+ qry=request.getParameter("get");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ getAttribute( writer, qry, name );
+ return;
+ }
+ qry=request.getParameter("qry");
+ if( qry == null ) {
+ qry = "*:*";
+ }
+
+ listBeans( writer, qry );
+
+ }
+
+ public void getAttribute(PrintWriter writer, String onameStr, String att) {
+ try {
+ ObjectName oname = new ObjectName(onameStr);
+ Object value = server.getAttribute(oname, att);
+ writer.println("OK - Attribute get '" + onameStr + "' - " + att
+ + "= " + escape(value.toString()));
+ } catch (Exception ex) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+ public void setAttribute( PrintWriter writer,
+ String onameStr, String att, String val )
+ {
+ try {
+ ObjectName oname=new ObjectName( onameStr );
+ String type = getType(oname, att);
+ if (type == null) {
+ writer.println("Not found");
+ return;
+ }
+ Object valueObj = convertValue(type, val );
+ server.setAttribute( oname, new Attribute(att, valueObj));
+ writer.println("OK - Attribute set");
+ } catch( Exception ex ) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+ public String getType( ObjectName oname, String attName )
+ {
+ String type=null;
+ MBeanInfo info=null;
+ try {
+ info=server.getMBeanInfo(oname);
+ } catch (Exception e) {
+ return null;
+ }
+
+ MBeanAttributeInfo attInfo[]=info.getAttributes();
+ for( int i=0; i<attInfo.length; i++ ) {
+ if( attName.equals(attInfo[i].getName())) {
+ type=attInfo[i].getType();
+ return type;
+ }
+ }
+ return null;
+ }
+
+ public Object convertValue(String type, String value)
+ {
+ Object objValue=value;
+
+ if( type==null || "java.lang.String".equals( type )) {
+ // string is default
+ objValue=value;
+ } else if( "javax.management.ObjectName".equals( type ) ||
+ "ObjectName".equals( type )) {
+ try {
+ objValue=new ObjectName( value );
+ } catch (MalformedObjectNameException e) {
+ return null;
+ }
+ } else if( "java.lang.Integer".equals( type ) ||
+ "int".equals( type )) {
+ objValue=new Integer( value );
+ } else if( "java.lang.Long".equals( type ) ||
+ "long".equals( type )) {
+ objValue=new Long( value );
+ } else if( "java.lang.Boolean".equals( type ) ||
+ "boolean".equals( type )) {
+ objValue=new Boolean( value );
+ }
+ return objValue;
+ }
+
+
+ public void listBeans( PrintWriter writer, String qry )
+ {
+
+ Set<ObjectName> names = null;
+ try {
+ names=server.queryNames(new ObjectName(qry), null);
+ writer.println("OK - Number of results: " + names.size());
+ writer.println();
+ } catch (Exception e) {
+ writer.println("Error - " + e.toString());
+ return;
+ }
+
+ Iterator<ObjectName> it=names.iterator();
+ while( it.hasNext()) {
+ ObjectName oname=it.next();
+ writer.println( "Name: " + oname.toString());
+
+ try {
+ MBeanInfo minfo=server.getMBeanInfo(oname);
+ // can't be null - I thinl
+ String code=minfo.getClassName();
+ if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
+ code=(String)server.getAttribute(oname, "modelerType");
+ }
+ writer.println("modelerType: " + code);
+
+ MBeanAttributeInfo attrs[]=minfo.getAttributes();
+ Object value=null;
+
+ for( int i=0; i< attrs.length; i++ ) {
+ if( ! attrs[i].isReadable() ) continue;
+ if( ! isSupported( attrs[i].getType() )) continue;
+ String attName=attrs[i].getName();
+ if( attName.indexOf( "=") >=0 ||
+ attName.indexOf( ":") >=0 ||
+ attName.indexOf( " ") >=0 ) {
+ continue;
+ }
+
+ try {
+ value=server.getAttribute(oname, attName);
+ } catch( Throwable t) {
+ log("Error getting attribute " + oname +
+ " " + attName + " " + t.toString());
+ continue;
+ }
+ if( value==null ) continue;
+ if( "modelerType".equals( attName)) continue;
+ String valueString=value.toString();
+ writer.println( attName + ": " + escape(valueString));
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ writer.println();
+ }
+
+ }
+
+ public String escape(String value) {
+ // The only invalid char is \n
+ // We also need to keep the string short and split it with \nSPACE
+ // XXX TODO
+ int idx=value.indexOf( "\n" );
+ if( idx < 0 ) return value;
+
+ int prev=0;
+ StringBuffer sb=new StringBuffer();
+ while( idx >= 0 ) {
+ appendHead(sb, value, prev, idx);
+
+ sb.append( "\\n\n ");
+ prev=idx+1;
+ if( idx==value.length() -1 ) break;
+ idx=value.indexOf('\n', idx+1);
+ }
+ if( prev < value.length() )
+ appendHead( sb, value, prev, value.length());
+ return sb.toString();
+ }
+
+ private void appendHead( StringBuffer sb, String value, int start, int end) {
+ if (end < 1) return;
+
+ int pos=start;
+ while( end-pos > 78 ) {
+ sb.append( value.substring(pos, pos+78));
+ sb.append( "\n ");
+ pos=pos+78;
+ }
+ sb.append( value.substring(pos,end));
+ }
+
+ public boolean isSupported( String type ) {
+ return true;
+ }
+}
package org.apache.tomcat.integration.jmx;
+import java.lang.management.ManagementFactory;
import java.util.logging.Logger;
import org.apache.tomcat.integration.ObjectManager;
public JmxObjectManagerSpi() {
registry = Registry.getRegistry(null, null);
+ registry.setServer(ManagementFactory.getPlatformMBeanServer());
}
public void bind(String name, Object o) {
--- /dev/null
+/*
+ * 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.integration.jmx;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.apache.tomcat.integration.DynamicObject;
+import org.apache.tomcat.integration.ObjectManager;
+import org.apache.tomcat.lite.http.HttpRequest;
+import org.apache.tomcat.lite.http.HttpResponse;
+import org.apache.tomcat.lite.http.HttpWriter;
+import org.apache.tomcat.lite.http.HttpChannel.HttpService;
+
+/**
+ * Send all registered JMX objects and properties as JSON.
+ *
+ * Based on JMXProxy servlet, but:
+ * - Async handler instead of servlet - so it works with 'raw' connector
+ * - doesn't use JMX - integrates with the ObjectManager ( assuming OM
+ * provies a list of managed objects )
+ * - all the reflection magic from modeler is implemented here.
+ *
+ * @author Costin Manolache
+ */
+public class UJmxHandler implements HttpService {
+
+ private static Logger log = Logger.getLogger(UJmxHandler.class.getName());
+ private UJmxObjectManagerSpi jmx;
+
+ public UJmxHandler(UJmxObjectManagerSpi jmx) {
+ this.jmx = jmx;
+ }
+
+ public void getAttribute(PrintWriter writer, String onameStr, String att) {
+ try {
+
+ Object bean = jmx.objects.get(onameStr);
+ Class beanClass = bean.getClass();
+ DynamicObject ci = jmx.getClassInfo(beanClass);
+
+ Object value = ci.getAttribute(bean, att);
+ writer.println("OK - Attribute get '" + onameStr + "' - " + att
+ + "= " + escape(value.toString()));
+ } catch (Exception ex) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+
+ public void setAttribute( PrintWriter writer,
+ String onameStr, String att, String val )
+ {
+ try {
+ Object bean = jmx.objects.get(onameStr);
+ Class beanClass = bean.getClass();
+ DynamicObject ci = jmx.getClassInfo(beanClass);
+
+ ci.setProperty(bean, att, val);
+ writer.println("OK - Attribute set");
+ } catch( Exception ex ) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+ public void listBeans( PrintWriter writer, String qry, boolean json )
+ {
+ if (json) {
+ listBeansJson(writer, qry);
+ return;
+ }
+ Set<String> names = jmx.objects.keySet();
+ writer.println("OK - Number of results: " + names.size());
+ writer.println();
+
+ Iterator<String> it=names.iterator();
+ while( it.hasNext()) {
+ String oname=it.next();
+ if (qry != null && oname.indexOf(qry) < 0) {
+ continue;
+ }
+ writer.println( "Name: " + oname);
+
+ try {
+ Object bean = jmx.objects.get(oname);
+
+ Class beanClass = bean.getClass();
+ DynamicObject ci = jmx.getClassInfo(beanClass);
+
+ writer.println("modelerType: " + beanClass.getName());
+
+ Object value=null;
+ for (String attName: ci.attributeNames()) {
+ try {
+ value = ci.getAttribute(bean, attName);
+ } catch( Throwable t) {
+ System.err.println("Error getting attribute " + oname +
+ " " + attName + " " + t.toString());
+ continue;
+ }
+ if( value==null ) continue;
+ String valueString=value.toString();
+ writer.println( attName + ": " + escape(valueString));
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ writer.println();
+ }
+
+ }
+
+ private static void json(PrintWriter writer, String name, String value) {
+ writer.write("\"" + name +"\":" + "\"" + escapeJson(value) + "\",");
+ }
+
+ private void listBeansJson(PrintWriter writer, String qry) {
+ Set<String> names = jmx.objects.keySet();
+ writer.println("[");
+
+ Iterator<String> it=names.iterator();
+ while( it.hasNext()) {
+ writer.print("{");
+ String oname=it.next();
+ json(writer, "name", oname);
+
+ try {
+ Object bean = jmx.objects.get(oname);
+ Class beanClass = bean.getClass();
+ DynamicObject ci = jmx.getClassInfo(beanClass);
+ json(writer, "modelerType", beanClass.getName());
+
+ Object value=null;
+ for (String attName: ci.attributeNames()) {
+ try {
+ value = ci.getAttribute(bean, attName);
+ } catch( Throwable t) {
+ System.err.println("Error getting attribute " + oname +
+ " " + attName + " " + t.toString());
+ continue;
+ }
+ if( value==null ) continue;
+ String valueString=value.toString();
+ json(writer, attName, valueString);
+ }
+ writer.println("}");
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ writer.println("]");
+ }
+
+ public static String escapeJson(String value) {
+ return value;
+ }
+
+ public static String escape(String value) {
+ // The only invalid char is \n
+ // We also need to keep the string short and split it with \nSPACE
+ // XXX TODO
+ int idx=value.indexOf( "\n" );
+ if( idx < 0 ) return value;
+
+ int prev=0;
+ StringBuffer sb=new StringBuffer();
+ while( idx >= 0 ) {
+ appendHead(sb, value, prev, idx);
+
+ sb.append( "\\n\n ");
+ prev=idx+1;
+ if( idx==value.length() -1 ) break;
+ idx=value.indexOf('\n', idx+1);
+ }
+ if( prev < value.length() )
+ appendHead( sb, value, prev, value.length());
+ return sb.toString();
+ }
+
+ private static void appendHead( StringBuffer sb, String value, int start, int end) {
+ if (end < 1) return;
+
+ int pos=start;
+ while( end-pos > 78 ) {
+ sb.append( value.substring(pos, pos+78));
+ sb.append( "\n ");
+ pos=pos+78;
+ }
+ sb.append( value.substring(pos,end));
+ }
+
+ public boolean isSupported( String type ) {
+ return true;
+ }
+
+ @Override
+ public void service(HttpRequest request, HttpResponse httpRes)
+ throws IOException {
+
+ httpRes.setContentType("text/plain");
+ HttpWriter out = httpRes.getBodyWriter();
+ PrintWriter writer = new PrintWriter(out);
+
+ String qry = request.getParameter("set");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ String val=request.getParameter("val");
+
+ setAttribute( writer, qry, name, val );
+ return;
+ }
+ qry=request.getParameter("get");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ getAttribute( writer, qry, name );
+ return;
+ }
+ qry=request.getParameter("qry");
+
+ listBeans( writer, qry, request.getParameter("json") != null);
+
+ }
+}
--- /dev/null
+/*
+ * 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.integration.jmx;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.apache.tomcat.integration.DynamicObject;
+import org.apache.tomcat.integration.ObjectManager;
+
+/**
+ * Send all registered JMX objects and properties as JSON.
+ *
+ * Based on JMXProxy servlet, but:
+ * - Async handler instead of servlet - so it works with 'raw' connector
+ * - doesn't use JMX - integrates with the ObjectManager ( assuming OM
+ * provies a list of managed objects )
+ * - all the reflection magic from modeler is implemented here.
+ *
+ * @author Costin Manolache
+ */
+public class UJmxObjectManagerSpi extends ObjectManager {
+
+ private static Logger log = Logger.getLogger(UJmxObjectManagerSpi.class.getName());
+
+ private ObjectManager om;
+
+ Map<Class, DynamicObject> types = new HashMap<Class, DynamicObject>();
+
+ Map<String, Object> objects = new HashMap();
+
+ @Override
+ public void bind(String name, Object o) {
+ if (objects.get(name) != null) {
+ log.warning("Duplicated name " + name);
+ }
+ objects.put(name, o);
+ }
+
+ @Override
+ public void unbind(String name) {
+ objects.remove(name);
+ }
+
+ // Dynamic
+ public void setObjectManager(ObjectManager om) {
+ this.om = om;
+ }
+
+ DynamicObject getClassInfo(Class beanClass) {
+ if (types.get(beanClass) != null) {
+ return types.get(beanClass);
+ }
+ DynamicObject res = new DynamicObject(beanClass);
+ types.put(beanClass, res);
+ return res;
+ }
+}
// can't call: cStream.free(); - will kill the adler, NPE
cStream = new ZStream();
// BEST_COMRESSION results in 256Kb per Deflate
- cStream.deflateInit(JZlib.Z_BEST_SPEED);
+ // 15 == default = 32k window
+ cStream.deflateInit(JZlib.Z_BEST_SPEED, 10);
dStream = new ZStream();
dStream.inflateInit();
private int requestCount = 0;
+ // dataReceived and endSendReceive
private Object readLock = new Object();
public Http11Connection(HttpConnector httpConnector) {
switchedProtocol.endSendReceive(http);
return;
}
+ chunk.recycle();
+ rchunk.recycle();
boolean keepAlive = keepAlive();
if (!keepAlive) {
if (debug) {
}
}
- synchronized (readLock) {
- requestCount++;
- beforeRequest();
- httpConnector.cpool.afterRequest(http, this, true);
- if (serverMode && keepAlive) {
- handleReceived(net); // will attempt to read next req
- }
+ requestCount++;
+ beforeRequest();
+ httpConnector.cpool.afterRequest(http, this, true);
+
+ if (serverMode && keepAlive) {
+ handleReceived(net); // will attempt to read next req
}
}
int rc = NEED_MORE;
// TODO: simplify, use readLine()
while (rc == NEED_MORE) {
- rc = chunk.parseChunkHeader(rawReceiveBuffers);
+ rc = rchunk.parseChunkHeader(rawReceiveBuffers);
if (rc == ERROR) {
http.abort("Chunk error");
receiveDone(http, body, true);
// used for chunk parsing/end
ChunkState chunk = new ChunkState();
+ ChunkState rchunk = new ChunkState();
static final int NEED_MORE = -1;
static final int ERROR = -4;
static final int DONE = -5;
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.http;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
+import org.apache.tomcat.lite.io.IOChannel;
+import org.apache.tomcat.lite.io.IOConnector;
+
+/**
+ * - Holds references to all active and kept-alive connections.
+ * - makes decisions on accepting more connections, closing old
+ * connections, etc
+ *
+ */
+public class HttpConnectionPool {
+ // TODO: add timeouts, limits per host/total, expire old entries
+
+ public static interface HttpConnectionPoolEvents {
+ public void newTarget(RemoteServer host);
+
+ public void targetRemoved(RemoteServer host);
+
+ public void newConnection(RemoteServer host, HttpConnection con);
+ public void closedConnection(RemoteServer host, HttpConnection con);
+ }
+
+ /**
+ * Connections for one remote host.
+ * This should't be restricted by IP:port or even hostname,
+ * for example if a server has multiple IPs or LB replicas - any would work.
+ */
+ public static class RemoteServer {
+ // all access sync on RemoteServer
+ private SpdyConnection spdy;
+
+ // all access sync on RemoteServer
+ private ArrayList<Http11Connection> connections
+ = new ArrayList<Http11Connection>();
+
+ Queue<HttpChannel> pending = new LinkedList<HttpChannel>();
+
+
+ // TODO: setter, default from connector
+ private int maxConnections = 20;
+
+ AtomicInteger activeRequests = new AtomicInteger();
+ AtomicInteger totalRequests = new AtomicInteger();
+ private volatile long lastActivity;
+
+ public String target;
+
+ public synchronized List<HttpConnector.HttpConnection> getConnections()
+ {
+ return new ArrayList<HttpConnection>(connections);
+ }
+
+ public synchronized Collection<HttpChannel> getActives() {
+ ArrayList<HttpChannel> actives = new ArrayList();
+ for (Http11Connection con: connections) {
+ if (con.activeHttp != null) {
+ actives.add(con.activeHttp);
+ }
+ }
+ if (spdy != null) {
+ actives.addAll(spdy.getActives());
+ }
+
+ return actives;
+ }
+
+ public synchronized void touch() {
+ lastActivity = System.currentTimeMillis();
+ }
+ }
+
+ private HttpConnectionPoolEvents poolEvents;
+
+ private static Logger log = Logger.getLogger("HttpConnector");
+
+ // visible for debugging - will be made private, with accessor
+ /**
+ * Map from client names to socket pools.
+ */
+ public Map<CharSequence, HttpConnectionPool.RemoteServer> hosts = new HashMap<CharSequence,
+ HttpConnectionPool.RemoteServer>();
+
+ // Statistics
+ public AtomicInteger waitingSockets = new AtomicInteger();
+ public AtomicInteger closedSockets = new AtomicInteger();
+
+ public AtomicInteger hits = new AtomicInteger();
+ public AtomicInteger misses = new AtomicInteger();
+ public AtomicInteger queued = new AtomicInteger();
+
+ public AtomicInteger activeRequests = new AtomicInteger();
+
+ private static boolean debug = false;
+ HttpConnector httpConnector;
+
+ public HttpConnectionPool(HttpConnector httpConnector) {
+ this.httpConnector = httpConnector;
+ }
+
+ public int getTargetCount() {
+ return hosts.size();
+ }
+
+ public int getSocketCount() {
+ return waitingSockets.get();
+ }
+
+ public int getClosedSockets() {
+ return closedSockets.get();
+ }
+
+ public Set<CharSequence> getKeepAliveTargets() {
+ return hosts.keySet();
+ }
+
+ public List<RemoteServer> getServers() {
+ return new ArrayList<RemoteServer>(hosts.values());
+ }
+
+ public void setEvents(HttpConnectionPoolEvents events) {
+ this.poolEvents = events;
+ }
+ /**
+ * Stop all cached connections.
+ */
+ public void clear() throws IOException {
+ synchronized (hosts) {
+ int active = 0;
+ for (RemoteServer rs: hosts.values()) {
+ synchronized (rs) {
+ int hostActive = 0;
+ if (rs.spdy != null) {
+ if (rs.spdy.channels.size() == 0) {
+ rs.spdy.close();
+ rs.spdy = null;
+ } else {
+ hostActive += rs.spdy.channels.size();
+ }
+ }
+ for (Http11Connection con: rs.connections) {
+ if (con.activeHttp == null) {
+ con.close();
+ } else {
+ hostActive++;
+ }
+ }
+ if (hostActive != rs.activeRequests.get()) {
+ log.warning("Active missmatch " + rs.target + " " +
+ hostActive + " "
+ + rs.activeRequests.get());
+ rs.activeRequests.set(hostActive);
+ }
+ active += hostActive;
+ }
+ }
+ if (active != this.activeRequests.get()) {
+ log.warning("Active missmatch " + active + " "
+ + activeRequests.get());
+ activeRequests.set(active);
+ }
+ }
+ }
+
+ /**
+ * Stop all active and cached connections
+ * @throws IOException
+ */
+ public void abort() throws IOException {
+ // TODO
+ clear();
+ hosts.clear();
+ }
+
+ /**
+ * @param key host:port, or some other key if multiple hosts:ips
+ * are connected to equivalent servers ( LB )
+ * @param httpCh
+ * @throws IOException
+ */
+ public void send(HttpChannel httpCh)
+ throws IOException {
+ String target = httpCh.getTarget();
+ HttpConnection con = null;
+ // TODO: check ssl on connection - now if a second request
+ // is received on a ssl connection - we just send it
+ boolean ssl = httpCh.getRequest().isSecure();
+
+ HttpConnectionPool.RemoteServer remoteServer = null;
+ synchronized (hosts) {
+ remoteServer = hosts.get(target);
+ if (remoteServer == null) {
+ remoteServer = new HttpConnectionPool.RemoteServer();
+ remoteServer.target = target;
+ hosts.put(target, remoteServer);
+ }
+ }
+
+ // TODO: remove old servers and connections
+
+ // Temp magic - until a better negotiation is defined
+ boolean forceSpdy = "SPDY/1.0".equals(httpCh.getRequest().getProtocol());
+ if (forceSpdy) {
+ // switch back the protocol
+ httpCh.getRequest().setProtocol("HTTP/1.1");
+ }
+
+ activeRequests.incrementAndGet();
+ remoteServer.activeRequests.incrementAndGet();
+
+ // if we already have a spdy connection or explicitely
+ // requested.
+ if (forceSpdy || remoteServer.spdy != null) {
+ synchronized (remoteServer) {
+ if (remoteServer.spdy == null) {
+ remoteServer.spdy = new SpdyConnection(httpConnector,
+ remoteServer);
+ }
+ con = remoteServer.spdy;
+ }
+
+ // Will be queued - multiple threads may try to send
+ // at the same time, and we need to queue anyways.
+ con.sendRequest(httpCh);
+ } else {
+ synchronized (remoteServer) {
+ Http11Connection hcon;
+ for (int i = 0; i < remoteServer.connections.size(); i++) {
+ hcon = (Http11Connection) remoteServer.connections.get(i);
+ if (hcon != null && hcon.activeHttp == null) {
+ hcon.beforeRequest(); // recycle
+
+ hcon.activeHttp = httpCh;
+ con = hcon;
+ break;
+ }
+ }
+ if (con == null) {
+// if (remoteServer.connections.size() > remoteServer.maxConnections) {
+// remoteServer.pending.add(httpCh);
+// queued.incrementAndGet();
+// if (debug) {
+// log.info("Queue: " + target + " " + remoteServer.connections.size());
+// }
+// return;
+// }
+ hcon = new Http11Connection(httpConnector);
+ hcon.setTarget(target);
+ hcon.activeHttp = httpCh;
+ hcon.remoteHost = remoteServer;
+ remoteServer.connections.add(hcon);
+ con = hcon;
+ }
+ }
+
+
+
+
+ // we got a connection - make sure we're connected
+ http11ConnectOrSend(httpCh, target, con, ssl);
+ }
+ }
+
+ private void http11ConnectOrSend(HttpChannel httpCh, String target,
+ HttpConnection con, boolean ssl) throws IOException {
+ httpCh.setConnection(con);
+
+ if (con.isOpen()) {
+ hits.incrementAndGet();
+ if (debug) {
+ httpCh.trace("HTTP_CONNECT: Reuse connection " + target + " " + this);
+ }
+ con.sendRequest(httpCh);
+ } else {
+ misses.incrementAndGet();
+ if (debug) {
+ httpCh.trace("HTTP_CONNECT: Start connection " + target + " " + this);
+ }
+ httpConnect(httpCh, target, ssl,
+ (Http11Connection) con);
+ }
+ }
+
+ void httpConnect(HttpChannel httpCh, String target,
+ boolean ssl, IOConnector.ConnectedCallback cb)
+ throws IOException {
+ if (debug) {
+ httpCh.trace("HTTP_CONNECT: New connection " + target);
+ }
+ String[] hostPort = target.split(":");
+
+ int targetPort = ssl ? 443 : 80;
+ if (hostPort.length > 1) {
+ targetPort = Integer.parseInt(hostPort[1]);
+ }
+
+ httpConnector.getIOConnector().connect(hostPort[0], targetPort,
+ cb);
+ }
+
+ public void afterRequest(HttpChannel http, HttpConnection con,
+ boolean keepAlive)
+ throws IOException {
+ activeRequests.decrementAndGet();
+ if (con.remoteHost != null) {
+ con.remoteHost.touch();
+ con.remoteHost.activeRequests.decrementAndGet();
+ }
+ if (con.serverMode) {
+ afterServerRequest(con, keepAlive);
+ } else {
+ afterClientRequest(con);
+ }
+ }
+
+ private void afterClientRequest(HttpConnection con)
+ throws IOException {
+ RemoteServer remoteServer = con.remoteHost;
+ HttpChannel req = null;
+
+ // If we have pending requests ( because too many active limit ), pick
+ // one and send it.
+ synchronized (remoteServer) {
+ // If closed - we can remove the object - or
+ // let a background thread do it, in case it's needed
+ // again.
+ if (remoteServer.pending.size() == 0) {
+ con.activeHttp = null;
+ if (debug) {
+ log.info("After request: no pending");
+ }
+ return;
+ }
+ req = remoteServer.pending.remove();
+ con.activeHttp = req;
+ if (debug) {
+ log.info("After request: send pending " + remoteServer.pending.size());
+ }
+ }
+
+ http11ConnectOrSend(req, con.getTarget().toString(),
+ con, req.getRequest().isSecure());
+ }
+
+ RemoteServer serverPool = new RemoteServer();
+
+ public void afterServerRequest(HttpConnection con, boolean keepAlive)
+ throws IOException {
+ con.activeHttp = null;
+ if (!keepAlive) {
+ synchronized (serverPool) {
+ // I could also reuse the object.
+ serverPool.connections.remove(con);
+ }
+ }
+ }
+
+ public HttpConnection accepted(IOChannel accepted) {
+ Http11Connection con = new Http11Connection(httpConnector);
+ con.remoteHost = serverPool;
+ synchronized (serverPool) {
+ serverPool.connections.add(con);
+ }
+ return con;
+ }
+
+
+ // Called by handleClosed
+ void stopKeepAlive(IOChannel schannel) {
+ CharSequence target = schannel.getTarget();
+ HttpConnectionPool.RemoteServer remoteServer = null;
+ synchronized (hosts) {
+ remoteServer = hosts.get(target);
+ if (remoteServer == null) {
+ return;
+ }
+ }
+ synchronized (remoteServer) {
+ if (remoteServer.connections.remove(schannel)) {
+ waitingSockets.decrementAndGet();
+ if (remoteServer.connections.size() == 0) {
+ hosts.remove(target);
+ }
+ }
+ }
+ }
+
+
+}
private Timer timer;
+ boolean compression = true;
+
private static Timer defaultTimer = new Timer(true);
public HttpConnector(IOConnector ioConnector) {
public void setDebugHttp(boolean b) {
this.debugHttp = b;
}
+
+ /**
+ * Allow or disable compression for this connector.
+ * Compression is enabled by default.
+ */
+ public HttpConnector setCompression(boolean b) {
+ this.compression = b;
+ return this;
+ }
public void setClientKeepAlive(boolean b) {
this.clientKeepAlive = b;
}
}
+
}
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.CBucket;
import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.util.buf.CharChunk;
/**
* Map used to represent headers and parameters ( could be used
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
.setDictionary(SPDY_DICT, DICT_ID);
IOBuffer headerCompressBuffer = new IOBuffer();
+ AtomicInteger inFrames = new AtomicInteger();
+ AtomicInteger inDataFrames = new AtomicInteger();
+ AtomicInteger inSyncStreamFrames = new AtomicInteger();
+ AtomicInteger inBytes = new AtomicInteger();
+
+ AtomicInteger outFrames = new AtomicInteger();
+ AtomicInteger outDataFrames = new AtomicInteger();
+ AtomicInteger outBytes = new AtomicInteger();
+
+
+ volatile boolean connecting = false;
+ volatile boolean connected = false;
+
// TODO: detect if it's spdy or http based on bit 8
@Override
}
}
- AtomicInteger inFrames = new AtomicInteger();
- AtomicInteger inDataFrames = new AtomicInteger();
- AtomicInteger inSyncStreamFrames = new AtomicInteger();
- AtomicInteger inBytes = new AtomicInteger();
- AtomicInteger outFrames = new AtomicInteger();
- AtomicInteger outDataFrames = new AtomicInteger();
- AtomicInteger outBytes = new AtomicInteger();
-
-
/**
* Frame received. Must consume all data for the frame.
return;
}
MultiMap mimeHeaders = http.getRequest().getMimeHeaders();
+
BBuffer headBuf = BBuffer.allocate();
-
SpdyConnection.appendShort(headBuf, mimeHeaders.size() + 3);
-
serializeMime(mimeHeaders, headBuf);
- if (headerCompression) {
- }
-
// TODO: url - with host prefix , method
// optimize...
SpdyConnection.appendAsciiHead(headBuf, "version");
SpdyConnection.appendAsciiHead(headBuf, "url");
// TODO: url
SpdyConnection.appendAsciiHead(headBuf, http.getRequest().requestURL());
-
+
+ if (headerCompression && httpConnector.compression) {
+ headerCompressBuffer.recycle();
+ headCompressOut.compress(headBuf, headerCompressBuffer, false);
+ headBuf.recycle();
+ headerCompressBuffer.copyAll(headBuf);
+ }
+
// Frame head - 8
BBuffer out = BBuffer.allocate();
// Syn-reply
http.channelId = 2 * lastOutStream.incrementAndGet() + 1;
}
SpdyConnection.appendInt(out, http.channelId);
+
http.setConnection(this);
synchronized (this) {
// TODO: send interrupt signal
}
-
-
- volatile boolean connecting = false;
- volatile boolean connected = false;
private boolean checkConnection(HttpChannel http) throws IOException {
public static BBuffer allocate(String msg) {
BBuffer bc = allocate();
- byte[] data = msg.getBytes(UTF8);
+ byte[] data = msg.getBytes();
bc.append(data, 0, data.length);
return bc;
}
public static BBuffer wrapper(String msg) {
BBuffer bc = new IOBucketWrap();
- byte[] data = msg.getBytes(UTF8);
+ byte[] data = msg.getBytes();
bc.setBytes(data, 0, data.length);
return bc;
}
}
public int indexOf(String src, int srcOff, int srcLen, int myOff) {
+ if ("".equals(src)) {
+ return myOff;
+ }
char first = src.charAt(srcOff);
// Look for first char
timer = new Timer(true);
}
+ public SocketConnector(int port) {
+ timer = new Timer(true);
+ }
+
/**
* This may be blocking - involves host resolution, connect.
* If the IP address is provided - it shouldn't block.
static int id = 0;
- synchronized NioThread getSelector() {
+ public synchronized NioThread getSelector() {
if (selector == null) {
String name = "SelectorThread-" + id++;
selector = new NioThread(name, true);
// we place the Buckets in the queue, as 'readable' buffers.
boolean newData = false;
try {
+ int read = 0;
synchronized(in) {
// data between 0 and position
int total = 0;
if (in.isAppendClosed()) { // someone closed me ?
ch.inputClosed(); // remove read interest.
// if outClosed - close completely
- super.sendHandleReceivedCallback();
- return;
+ newData = true;
+ break;
}
ByteBuffer bb = in.getWriteBuffer();
- int read = ch.read(bb);
+ read = ch.read(bb);
in.releaseWriteBuffer(read);
- if (in == null) {
- // Detached.
- if (newData) {
- sendHandleReceivedCallback();
- }
- return;
+ if (in == null) { // Detached.
+ break;
}
if (read < 0) {
// mark the in buffer as closed
in.close();
ch.inputClosed();
- sendHandleReceivedCallback();
- return;
+ newData = true;
+ break;
}
if (read == 0) {
- if (newData) {
- super.sendHandleReceivedCallback();
- }
- return;
+ break;
}
total += read;
newData = true;
}
+ } // sync
+ if (newData) {
+ super.sendHandleReceivedCallback();
}
+
} catch (Throwable t) {
close();
if (t instanceof IOException) {
protected BBucket mb;
protected boolean chunked = false;
+ int code = 200;
protected String contentType = "text/plain";
public StaticContentService() {
}
+ /**
+ * Used for testing chunked encoding.
+ * @return
+ */
public StaticContentService chunked() {
chunked = true;
return this;
mb = BBuffer.wrapper(data, 0, data.length);
return this;
}
+
+ public StaticContentService setStatus(int status) {
+ this.code = status;
+ return this;
+ }
public StaticContentService withLen(int len) {
byte[] data = new byte[len];
@Override
public void service(HttpRequest httpReq, HttpResponse res) throws IOException {
- res.setStatus(200);
+ res.setStatus(code);
if (!chunked) {
res.setContentLength(mb.remaining());
}
res.setContentType(contentType);
+ int len = mb.remaining();
+ int first = 0;
+
if (chunked) {
+ first = len / 2;
res.getBody()
- .queue(BBuffer.wrapper(mb, 0, mb.remaining()));
+ .queue(BBuffer.wrapper(mb, 0, first));
res.flush();
}
- res.getBody().queue(BBuffer.wrapper(mb, 0, mb.remaining()));
+ res.getBody().queue(BBuffer.wrapper(mb, 0, len - first));
}
}
\ No newline at end of file
+++ /dev/null
-/*
- * 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.lite.service;
-
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.integration.DynamicObject;
-import org.apache.tomcat.integration.ObjectManager;
-
-/**
- * Send all registered JMX objects and properties as JSON.
- *
- * Based on JMXProxy servlet, but:
- * - Async handler instead of servlet - so it works with 'raw' connector
- * - doesn't use JMX - integrates with the ObjectManager ( assuming OM
- * provies a list of managed objects )
- * - all the reflection magic from modeler is implemented here.
- *
- * @author Costin Manolache
- */
-public class JMXProxy extends ObjectManager implements Runnable {
-
- static Logger log = Logger.getLogger(JMXProxy.class.getName());
-
- protected ObjectManager om;
-
- Map<Class, DynamicObject> types = new HashMap<Class, DynamicObject>();
-
- Map<String, Object> objects = new HashMap();
-
-
- public void bind(String name, Object o) {
- objects.put(name, o);
- }
-
- public void unbind(String name) {
- objects.remove(name);
- }
-
-
- public void setObjectManager(ObjectManager om) {
- this.om = om;
- }
-
-
- private DynamicObject getClassInfo(Class beanClass) {
- if (types.get(beanClass) != null) {
- return types.get(beanClass);
- }
- DynamicObject res = new DynamicObject(beanClass);
- types.put(beanClass, res);
- return res;
- }
-
-
- // --------------------------------------------------------- Public Methods
-
- public void getAttribute(PrintWriter writer, String onameStr, String att) {
- try {
-
- Object bean = objects.get(onameStr);
- Class beanClass = bean.getClass();
- DynamicObject ci = getClassInfo(beanClass);
-
- Object value = ci.getAttribute(bean, att);
- writer.println("OK - Attribute get '" + onameStr + "' - " + att
- + "= " + escape(value.toString()));
- } catch (Exception ex) {
- writer.println("Error - " + ex.toString());
- }
- }
-
-
- public void setAttribute( PrintWriter writer,
- String onameStr, String att, String val )
- {
- try {
- Object bean = objects.get(onameStr);
- Class beanClass = bean.getClass();
- DynamicObject ci = getClassInfo(beanClass);
-
- ci.setProperty(bean, att, val);
- writer.println("OK - Attribute set");
- } catch( Exception ex ) {
- writer.println("Error - " + ex.toString());
- }
- }
-
- public void listBeans( PrintWriter writer, String qry, boolean json )
- {
- if (json) {
- listBeansJson(writer, qry);
- return;
- }
- Set<String> names = objects.keySet();
- writer.println("OK - Number of results: " + names.size());
- writer.println();
-
- Iterator<String> it=names.iterator();
- while( it.hasNext()) {
- String oname=it.next();
- writer.println( "Name: " + oname);
-
- try {
- Object bean = objects.get(oname);
- Class beanClass = bean.getClass();
- DynamicObject ci = getClassInfo(beanClass);
- writer.println("modelerType: " + beanClass.getName());
-
- Object value=null;
- for (String attName: ci.attributeNames()) {
- try {
- value = ci.getAttribute(bean, attName);
- } catch( Throwable t) {
- System.err.println("Error getting attribute " + oname +
- " " + attName + " " + t.toString());
- continue;
- }
- if( value==null ) continue;
- String valueString=value.toString();
- writer.println( attName + ": " + escape(valueString));
- }
- } catch (Exception e) {
- // Ignore
- }
- writer.println();
- }
-
- }
-
- private static void json(PrintWriter writer, String name, String value) {
- writer.write("\"" + name +"\":" + "\"" + escapeJson(value) + "\",");
- }
-
- private void listBeansJson(PrintWriter writer, String qry) {
- Set<String> names = objects.keySet();
- writer.println("[");
-
- Iterator<String> it=names.iterator();
- while( it.hasNext()) {
- writer.print("{");
- String oname=it.next();
- json(writer, "name", oname);
-
- try {
- Object bean = objects.get(oname);
- Class beanClass = bean.getClass();
- DynamicObject ci = getClassInfo(beanClass);
- json(writer, "modelerType", beanClass.getName());
-
- Object value=null;
- for (String attName: ci.attributeNames()) {
- try {
- value = ci.getAttribute(bean, attName);
- } catch( Throwable t) {
- System.err.println("Error getting attribute " + oname +
- " " + attName + " " + t.toString());
- continue;
- }
- if( value==null ) continue;
- String valueString=value.toString();
- json(writer, attName, valueString);
- }
- writer.println("}");
- } catch (Exception e) {
- // Ignore
- }
- }
- writer.println("]");
- }
-
- public static String escapeJson(String value) {
- return value;
- }
-
- public static String escape(String value) {
- // The only invalid char is \n
- // We also need to keep the string short and split it with \nSPACE
- // XXX TODO
- int idx=value.indexOf( "\n" );
- if( idx < 0 ) return value;
-
- int prev=0;
- StringBuffer sb=new StringBuffer();
- while( idx >= 0 ) {
- appendHead(sb, value, prev, idx);
-
- sb.append( "\\n\n ");
- prev=idx+1;
- if( idx==value.length() -1 ) break;
- idx=value.indexOf('\n', idx+1);
- }
- if( prev < value.length() )
- appendHead( sb, value, prev, value.length());
- return sb.toString();
- }
-
- private static void appendHead( StringBuffer sb, String value, int start, int end) {
- if (end < 1) return;
-
- int pos=start;
- while( end-pos > 78 ) {
- sb.append( value.substring(pos, pos+78));
- sb.append( "\n ");
- pos=pos+78;
- }
- sb.append( value.substring(pos,end));
- }
-
- public boolean isSupported( String type ) {
- return true;
- }
-
- @Override
- public void run() {
-
- }
-}
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
+import org.apache.tomcat.integration.jmx.JMXProxyServlet;
import org.apache.tomcat.lite.http.HttpChannel;
import org.apache.tomcat.lite.http.HttpRequest;
import org.apache.tomcat.lite.http.HttpResponse;
import org.apache.tomcat.lite.http.MappingData;
+import org.apache.tomcat.lite.io.WrappedException;
import org.apache.tomcat.servlets.jsp.BaseJspLoader;
import org.apache.tomcat.servlets.util.Enumerator;
ctx.lite.notifyAdd(this);
}
+ public ServletConfigImpl(Servlet realServlet) throws IOException {
+ instance = realServlet;
+ try {
+ realServlet.init(this);
+ } catch (ServletException e) {
+ throw new WrappedException(e);
+ }
+ }
+
/**
* Return the available date/time for this servlet, in milliseconds since
* the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
ServletResponseImpl res = req.getResponse();
// TODO
+ try {
+ instance.service(req, res);
+ } catch (ServletException e) {
+ throw new WrappedException(e);
+ }
}
/** Coyote / mapper adapter. Result of the mapper.
+++ /dev/null
-/*
- * 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.servlets.jmx;
-
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.management.ManagementFactory;
-import java.util.Iterator;
-import java.util.Set;
-import javax.management.MBeanServer;
-import javax.management.MBeanServerFactory;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import javax.management.MBeanInfo;
-import javax.management.MBeanAttributeInfo;
-import javax.management.Attribute;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * This servlet will dump JMX attributes in a simple format
- * and implement proxy services for modeler.
- *
- * @author Costin Manolache
- */
-public class JMXProxyServlet extends HttpServlet {
- protected MBeanServer server = null;
-
- /**
- * Initialize this servlet.
- */
- public void init() throws ServletException {
- // Retrieve the MBean serverif (server == null) {
- if( MBeanServerFactory.findMBeanServer(null).size() > 0 ) {
- server = MBeanServerFactory.findMBeanServer(null).get(0);
- } else {
- server = ManagementFactory.getPlatformMBeanServer();
- }
- }
-
-
- /**
- * Process a GET request for the specified resource.
- *
- * @param request The servlet request we are processing
- * @param response The servlet response we are creating
- *
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet-specified error occurs
- */
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
-
- response.setContentType("text/plain");
-
- PrintWriter writer = response.getWriter();
-
- if( server==null ) {
- writer.println("Error - No mbean server");
- return;
- }
-
- String qry=request.getParameter("set");
- if( qry!= null ) {
- String name=request.getParameter("att");
- String val=request.getParameter("val");
-
- setAttribute( writer, qry, name, val );
- return;
- }
- qry=request.getParameter("get");
- if( qry!= null ) {
- String name=request.getParameter("att");
- getAttribute( writer, qry, name );
- return;
- }
- qry=request.getParameter("qry");
- if( qry == null ) {
- qry = "*:*";
- }
-
- listBeans( writer, qry );
-
- }
-
- public void getAttribute(PrintWriter writer, String onameStr, String att) {
- try {
- ObjectName oname = new ObjectName(onameStr);
- Object value = server.getAttribute(oname, att);
- writer.println("OK - Attribute get '" + onameStr + "' - " + att
- + "= " + escape(value.toString()));
- } catch (Exception ex) {
- writer.println("Error - " + ex.toString());
- }
- }
-
- public void setAttribute( PrintWriter writer,
- String onameStr, String att, String val )
- {
- try {
- ObjectName oname=new ObjectName( onameStr );
- String type = getType(oname, att);
- if (type == null) {
- writer.println("Not found");
- return;
- }
- Object valueObj = convertValue(type, val );
- server.setAttribute( oname, new Attribute(att, valueObj));
- writer.println("OK - Attribute set");
- } catch( Exception ex ) {
- writer.println("Error - " + ex.toString());
- }
- }
-
- public String getType( ObjectName oname, String attName )
- {
- String type=null;
- MBeanInfo info=null;
- try {
- info=server.getMBeanInfo(oname);
- } catch (Exception e) {
- return null;
- }
-
- MBeanAttributeInfo attInfo[]=info.getAttributes();
- for( int i=0; i<attInfo.length; i++ ) {
- if( attName.equals(attInfo[i].getName())) {
- type=attInfo[i].getType();
- return type;
- }
- }
- return null;
- }
-
- public Object convertValue(String type, String value)
- {
- Object objValue=value;
-
- if( type==null || "java.lang.String".equals( type )) {
- // string is default
- objValue=value;
- } else if( "javax.management.ObjectName".equals( type ) ||
- "ObjectName".equals( type )) {
- try {
- objValue=new ObjectName( value );
- } catch (MalformedObjectNameException e) {
- return null;
- }
- } else if( "java.lang.Integer".equals( type ) ||
- "int".equals( type )) {
- objValue=new Integer( value );
- } else if( "java.lang.Long".equals( type ) ||
- "long".equals( type )) {
- objValue=new Long( value );
- } else if( "java.lang.Boolean".equals( type ) ||
- "boolean".equals( type )) {
- objValue=new Boolean( value );
- }
- return objValue;
- }
-
-
- public void listBeans( PrintWriter writer, String qry )
- {
-
- Set<ObjectName> names = null;
- try {
- names=server.queryNames(new ObjectName(qry), null);
- writer.println("OK - Number of results: " + names.size());
- writer.println();
- } catch (Exception e) {
- writer.println("Error - " + e.toString());
- return;
- }
-
- Iterator<ObjectName> it=names.iterator();
- while( it.hasNext()) {
- ObjectName oname=it.next();
- writer.println( "Name: " + oname.toString());
-
- try {
- MBeanInfo minfo=server.getMBeanInfo(oname);
- // can't be null - I thinl
- String code=minfo.getClassName();
- if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
- code=(String)server.getAttribute(oname, "modelerType");
- }
- writer.println("modelerType: " + code);
-
- MBeanAttributeInfo attrs[]=minfo.getAttributes();
- Object value=null;
-
- for( int i=0; i< attrs.length; i++ ) {
- if( ! attrs[i].isReadable() ) continue;
- if( ! isSupported( attrs[i].getType() )) continue;
- String attName=attrs[i].getName();
- if( attName.indexOf( "=") >=0 ||
- attName.indexOf( ":") >=0 ||
- attName.indexOf( " ") >=0 ) {
- continue;
- }
-
- try {
- value=server.getAttribute(oname, attName);
- } catch( Throwable t) {
- log("Error getting attribute " + oname +
- " " + attName + " " + t.toString());
- continue;
- }
- if( value==null ) continue;
- if( "modelerType".equals( attName)) continue;
- String valueString=value.toString();
- writer.println( attName + ": " + escape(valueString));
- }
- } catch (Exception e) {
- // Ignore
- }
- writer.println();
- }
-
- }
-
- public String escape(String value) {
- // The only invalid char is \n
- // We also need to keep the string short and split it with \nSPACE
- // XXX TODO
- int idx=value.indexOf( "\n" );
- if( idx < 0 ) return value;
-
- int prev=0;
- StringBuffer sb=new StringBuffer();
- while( idx >= 0 ) {
- appendHead(sb, value, prev, idx);
-
- sb.append( "\\n\n ");
- prev=idx+1;
- if( idx==value.length() -1 ) break;
- idx=value.indexOf('\n', idx+1);
- }
- if( prev < value.length() )
- appendHead( sb, value, prev, value.length());
- return sb.toString();
- }
-
- private void appendHead( StringBuffer sb, String value, int start, int end) {
- if (end < 1) return;
-
- int pos=start;
- while( end-pos > 78 ) {
- sb.append( value.substring(pos, pos+78));
- sb.append( "\n ");
- pos=pos+78;
- }
- sb.append( value.substring(pos,end));
- }
-
- public boolean isSupported( String type ) {
- return true;
- }
-}
+++ /dev/null
-/*
- */
-package org.apache.tomcat.servlets.jmx;
-
-import java.util.logging.Logger;
-
-import org.apache.tomcat.integration.ObjectManager;
-import org.apache.tomcat.util.modeler.Registry;
-
-/**
- * Plugin for integration with JMX.
- *
- * All objects of interest are registered automatically.
- */
-public class JmxObjectManagerSpi extends ObjectManager {
- Registry registry;
- Logger log = Logger.getLogger("JmxObjectManager");
-
- public JmxObjectManagerSpi() {
- registry = Registry.getRegistry(null, null);
- }
-
- public void bind(String name, Object o) {
- try {
- registry.registerComponent(o,
- ":name=\"" + name + "\"", null);
- } catch (Exception e) {
- log.severe("Error registering" + e);
- }
- }
-
- public void unbind(String name) {
- registry.unregisterComponent(":name=\"" + name + "\"");
- }
-
- @Override
- public Object get(String key) {
- return null;
- }
-
-}
import java.net.HttpURLConnection;
import java.net.URL;
+import org.apache.tomcat.integration.jmx.JMXProxyServlet;
+import org.apache.tomcat.integration.jmx.JmxObjectManagerSpi;
+import org.apache.tomcat.integration.jmx.UJmxHandler;
+import org.apache.tomcat.integration.jmx.UJmxObjectManagerSpi;
import org.apache.tomcat.integration.simple.Main;
import org.apache.tomcat.integration.simple.SimpleObjectManager;
import org.apache.tomcat.lite.http.BaseMapper;
import org.apache.tomcat.lite.http.DefaultHttpConnector;
import org.apache.tomcat.lite.http.Dispatcher;
import org.apache.tomcat.lite.http.HttpChannel;
+import org.apache.tomcat.lite.http.HttpConnectionPool;
import org.apache.tomcat.lite.http.HttpConnector;
import org.apache.tomcat.lite.http.HttpRequest;
import org.apache.tomcat.lite.http.HttpResponse;
import org.apache.tomcat.lite.http.HttpChannel.HttpService;
+import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
import org.apache.tomcat.lite.http.HttpConnector.HttpChannelEvents;
+import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
import org.apache.tomcat.lite.http.services.EchoCallback;
import org.apache.tomcat.lite.http.services.SleepCallback;
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.proxy.HttpProxyService;
import org.apache.tomcat.lite.proxy.StaticContentService;
import org.apache.tomcat.lite.service.IOStatus;
+import org.apache.tomcat.lite.servlet.ServletConfigImpl;
import org.apache.tomcat.util.buf.ByteChunk;
/**
private HttpConnector testProxy = new HttpConnector(serverCon);
private HttpProxyService proxy;
-
+
+ UJmxObjectManagerSpi jmx = new UJmxObjectManagerSpi();
+
public static TestMain shared() {
if (defaultServer == null) {
defaultServer = new TestMain();
return shared().testClient;
}
- public void initTestCallback(Dispatcher d) {
+ public void initTestCallback(Dispatcher d) throws IOException {
BaseMapper.ContextMapping mCtx = d.addContext(null, "", null, null, null, null);
d.addWrapper(mCtx, "/", new StaticContentService()
"<a href='/proc/cpool/proxy'>Proxy pool</a><br/>" +
""));
+ d.addWrapper(mCtx, "/favicon.ico",
+ new StaticContentService().setStatus(404).setData("Not found"));
+
d.addWrapper(mCtx, "/hello", new StaticContentService().setData("Hello world"));
d.addWrapper(mCtx, "/2nd", new StaticContentService().setData("Hello world2"));
d.addWrapper(mCtx, "/echo/*", new EchoCallback());
}
});
+ d.addWrapper(mCtx, "/ujmx", new UJmxHandler(jmx));
+ d.addWrapper(mCtx, "/jmx",
+ new ServletConfigImpl(new JMXProxyServlet()));
}
public void run() {
}
}
+ public int getServerPort() {
+ return 8802;
+ }
+
+ public int getProxyPort() {
+ return 8903;
+ }
+
+ public int getSslServerPort() {
+ return 8443;
+ }
+
protected void startAll(int basePort) throws IOException {
int port = basePort + 903;
if (proxy == null) {
+
proxy = new HttpProxyService()
.withHttpClient(testClient);
testProxy.setPort(port);
// testServer.setDebug(true);
// sslServer.setDebug(true);
// sslServer.setDebugHttp(true);
-
+
+ // Bind the objects, make them visible in JMX
+ // additional settings from config
+ initObjectManager("org/apache/tomcat/lite/test.properties");
}
Runtime.getRuntime().addShutdownHook(new Thread() {
}
});
}
+
+ public void bindConnector(HttpConnector con, final String base) {
+ om.bind("HttpConnector-" + base, con);
+ om.bind("HttpConnectionPool-" + base, con.cpool);
+ SocketConnector sc = (SocketConnector) con.getIOConnector();
+ om.bind("NioThread-" + base, sc.getSelector());
+
+ con.cpool.setEvents(new HttpConnectionPool.HttpConnectionPoolEvents() {
+ @Override
+ public void closedConnection(RemoteServer host, HttpConnection con) {
+ om.unbind("HttpConnection-" + base + "-" + con.getId());
+ }
+
+ @Override
+ public void newConnection(RemoteServer host, HttpConnection con) {
+ om.bind("HttpConnection-" + base + "-" + con.getId(), con);
+ }
+
+ @Override
+ public void newTarget(RemoteServer host) {
+ om.bind("AsyncHttp-" + base + "-" + host.target, host);
+ }
+
+ @Override
+ public void targetRemoved(RemoteServer host) {
+ om.unbind("AsyncHttp-" + base + "-" + host.target);
+ }
+
+ });
+
+ con.setOnCreate(new HttpChannelEvents() {
+ @Override
+ public void onCreate(HttpChannel data, HttpConnector extraData)
+ throws IOException {
+ om.bind("AsyncHttp-" + base + "-" + data.getId(), data);
+ }
+ @Override
+ public void onDestroy(HttpChannel data, HttpConnector extraData)
+ throws IOException {
+ om.unbind("AsyncHttp-" + base + "-" + data.getId());
+ }
+ });
+
+
+ }
+
private void initObjectManager(String cfgFile) {
if (om == null) {
om = new SimpleObjectManager();
}
-
+ // All objects visible in JMX via util.registry
+ // ( optional dependency )
+ om.register(new JmxObjectManagerSpi());
+ om.register(jmx);
+
+
om.loadResource(cfgFile);
String run = (String) om.getProperty("RUN");
String[] runNames = run == null ? new String[] {} : run.split(",");
}
}
- om.bind("HttpConnector-TestServer", testServer);
- om.bind("HttpConnector", testClient);
- om.bind("HttpConnector-Proxy", testProxy);
+ bindConnector(testServer, "TestServer");
+ bindConnector(testClient, "Client");
+ bindConnector(testProxy, "Proxy");
- testServer.setOnCreate(new HttpChannelEvents() {
- @Override
- public void onCreate(HttpChannel data, HttpConnector extraData)
- throws IOException {
- //data.trace("BIND");
- om.bind("AsyncHttp-" + data.getId(), data);
- }
- @Override
- public void onDestroy(HttpChannel data, HttpConnector extraData)
- throws IOException {
- //data.trace("UNBIND");
- om.unbind("AsyncHttp-" + data.getId());
- }
- });
}
TestMain testMain = new TestMain();
TestMain.defaultServer = testMain;
testMain.om = new SimpleObjectManager(args);
- testMain.initObjectManager("org/apache/tomcat/lite/test.properties");
testMain.run();
Main.waitStop();
}
import org.apache.tomcat.lite.http.HttpChannel;
import org.apache.tomcat.lite.http.HttpConnector;
import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.SpdyConnection;
import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
+import org.apache.tomcat.lite.io.SocketConnector;
import org.apache.tomcat.util.buf.ByteChunk;
/*
public class LiveHttpThreadedTest extends TestCase {
HttpConnector clientCon = TestMain.shared().getClient();
HttpConnector serverCon = TestMain.shared().getTestServer();
+
+ HttpConnector spdyClient =
+ new HttpConnector(new SocketConnector()).setCompression(false);
+
+ HttpConnector spdyClientCompress =
+ new HttpConnector(new SocketConnector());
+
+ HttpConnector spdyClientCompressSsl =
+ new HttpConnector(new SocketConnector());
+
ThreadRunner tr;
static MBeanServer server;
-
+ static boolean dumpHeap = false;
+
AtomicInteger ok = new AtomicInteger();
Object lock = new Object();
int reqCnt;
public void test1000Async() throws Exception {
try {
- asyncRequest(10, 100, false);
+ asyncRequest(10, 100, false, clientCon);
} finally {
dumpHeap("heapAsync.bin");
}
public void test10000Async() throws Exception {
try {
- asyncRequest(20, 500, false);
+ asyncRequest(20, 500, false, clientCon);
} finally {
dumpHeap("heapAsyncBig.bin");
}
public void test1000AsyncSpdy() throws Exception {
try {
- asyncRequest(10, 100, true);
+ asyncRequest(10, 100, true, spdyClient);
} finally {
dumpHeap("heapSpdy1000.bin");
}
public void test10000AsyncSpdy() throws Exception {
try {
- asyncRequest(20, 500, true);
+ asyncRequest(20, 500, true, spdyClient);
+ } finally {
+ dumpHeap("heapSpdy10000.bin");
+ }
+ }
+
+ public void test1000AsyncSpdyComp() throws Exception {
+ try {
+ asyncRequest(10, 100, true, spdyClientCompress);
+ } finally {
+ dumpHeap("heapSpdy1000Comp.bin");
+ }
+
+ }
+
+ public void test10000AsyncSpdyComp() throws Exception {
+ try {
+ asyncRequest(20, 500, true, spdyClientCompress);
} finally {
dumpHeap("heapSpdy10000.bin");
}
}
public void asyncRequest(int thr, int perthr,
- final boolean spdy) throws Exception {
+ final boolean spdy, final HttpConnector clientCon) throws Exception {
reqCnt = thr * perthr;
long t0 = System.currentTimeMillis();
tr = new ThreadRunner(thr, perthr) {
urlRequest(10, 100);
}
- public void testURLRequest10000() throws Exception {
+ public void xtestURLRequest10000() throws Exception {
urlRequest(20, 500);
}
// TODO: move to a servlet
private void dumpHeap(String file) throws InstanceNotFoundException,
MBeanException, ReflectionException, MalformedObjectNameException {
-
+ if (!dumpHeap) {
+ return;
+ }
if (server == null) {
server = ManagementFactory.getPlatformMBeanServer();
String resStr =
TestMain.get("http://localhost:8903/chunked/test")
.toString();
- assertEquals(8, resStr.length());
+ assertEquals(4, resStr.length());
assertTrue(resStr.indexOf("AAA") >= 0);
}
-RUN=Log,JMXProxy,Socks,TomcatLite
+RUN=Log,Socks,TomcatLite
Log.(class)=org.apache.tomcat.lite.service.LogConfig
Log.debug=org.apache.tomcat.lite.http.HttpConnector
Log.debug=Proxy
-JMXProxy.(class)=org.apache.tomcat.lite.service.JMXProxy
-JMXProxy.port=8003
-
Socks.(class)=org.apache.tomcat.lite.proxy.SocksServer
Socks.port=2080
Socks.idleTimeout=0