Few more fixes after running the load tests, more monitoring.
authorcostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 19 Jan 2010 22:17:13 +0000 (22:17 +0000)
committercostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 19 Jan 2010 22:17:13 +0000 (22:17 +0000)
The connection pool is now a top level class, a lot of fixes to handle spdy and http mixing.
Added support for header compression in SPDY requests, reduced window size.
Got it to compile again on android ( the UTF8 in getBytes - will add it back, but using the char-byte convertor )

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@900979 13f79535-47bb-0310-9956-ffa450edef68

24 files changed:
modules/tomcat-lite/.classpath
modules/tomcat-lite/java/org/apache/tomcat/integration/DynamicObject.java
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/JMXProxyServlet.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/JmxObjectManagerSpi.java
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxHandler.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxObjectManagerSpi.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/http/CompressFilter.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/Http11Connection.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnector.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/MultiMap.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/SpdyConnection.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketConnector.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java
modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/lite/servlet/ServletConfigImpl.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JMXProxyServlet.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JmxObjectManagerSpi.java [deleted file]
modules/tomcat-lite/test/org/apache/tomcat/lite/TestMain.java
modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttpThreadedTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/ProxyTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/test.properties

index bfb9921..9cb14db 100644 (file)
@@ -11,7 +11,7 @@
        <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"/>
@@ -19,7 +19,7 @@
        <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>
index 7afcb1f..d7c5128 100644 (file)
@@ -2,6 +2,7 @@
  */
 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;
@@ -29,9 +30,9 @@ import java.util.logging.Logger;
 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];
@@ -47,21 +48,18 @@ public class DynamicObject {
     
     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) {
@@ -94,52 +92,18 @@ public class DynamicObject {
                 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());
     }
 
 
@@ -154,28 +118,44 @@ public class DynamicObject {
     }
 
     // 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) {
@@ -203,6 +183,7 @@ public class DynamicObject {
     }
 
     public boolean setProperty(Object proxy, String name, String value) {
+        // TODO: use the cache...
         String setter = "set" + capitalize(name);
 
         try {
@@ -389,5 +370,16 @@ public class DynamicObject {
         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;
+    }
+    
     
 }
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/JMXProxyServlet.java b/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/JMXProxyServlet.java
new file mode 100644 (file)
index 0000000..0e653c7
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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;
+    }
+}
index b70127f..326bb57 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.tomcat.integration.jmx;
 
+import java.lang.management.ManagementFactory;
 import java.util.logging.Logger;
 
 import org.apache.tomcat.integration.ObjectManager;
@@ -33,6 +34,7 @@ public class JmxObjectManagerSpi extends ObjectManager {
     
     public JmxObjectManagerSpi() {
         registry = Registry.getRegistry(null, null);
+        registry.setServer(ManagementFactory.getPlatformMBeanServer());
     }
     
     public void bind(String name, Object o) {
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxHandler.java b/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxHandler.java
new file mode 100644 (file)
index 0000000..c5a2e76
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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);
+
+    }
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxObjectManagerSpi.java b/modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxObjectManagerSpi.java
new file mode 100644 (file)
index 0000000..3ba23e1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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;
+    }
+}
index ffeb036..d78c55e 100644 (file)
@@ -57,7 +57,8 @@ public class CompressFilter {
         // 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();
index 7794fad..2571a85 100644 (file)
@@ -67,6 +67,7 @@ public class Http11Connection extends HttpConnection
 
     private int requestCount = 0;
 
+    // dataReceived and endSendReceive
     private Object readLock = new Object();
     
     public Http11Connection(HttpConnector httpConnector) {
@@ -318,6 +319,8 @@ public class Http11Connection extends HttpConnection
             switchedProtocol.endSendReceive(http);
             return;
         }
+        chunk.recycle();
+        rchunk.recycle();
         boolean keepAlive = keepAlive();
         if (!keepAlive) {
             if (debug) {
@@ -329,14 +332,13 @@ public class Http11Connection extends HttpConnection
 
             }
         }
-        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
         }
     }
     
@@ -680,7 +682,7 @@ public class Http11Connection extends HttpConnection
                     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);
@@ -1218,6 +1220,7 @@ public class Http11Connection extends HttpConnection
 
     // 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;
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java
new file mode 100644 (file)
index 0000000..363ac59
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ */
+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);
+                }
+            }
+        }
+    }
+
+
+}
index 42313ab..63a6572 100644 (file)
@@ -91,6 +91,8 @@ public class HttpConnector {
 
     private Timer timer;
 
+    boolean compression = true;
+
     private static Timer defaultTimer = new Timer(true);
     
     public HttpConnector(IOConnector ioConnector) {
@@ -125,6 +127,15 @@ public class HttpConnector {
     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;
@@ -472,4 +483,5 @@ public class HttpConnector {
         }
         
     }
+
 }
index 515c2df..97fca7c 100644 (file)
@@ -27,7 +27,6 @@ import java.util.Map;
 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 
index 9cdb95f..e4c2a77 100644 (file)
@@ -5,10 +5,7 @@ package org.apache.tomcat.lite.http;
 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;
@@ -102,6 +99,19 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         .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
@@ -139,16 +149,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         }
     }
 
-    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.
@@ -367,15 +368,11 @@ public class SpdyConnection extends HttpConnector.HttpConnection
             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");
@@ -387,7 +384,14 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         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 
@@ -417,6 +421,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
             http.channelId = 2 * lastOutStream.incrementAndGet() + 1;            
         }
         SpdyConnection.appendInt(out, http.channelId);
+        
         http.setConnection(this);
 
         synchronized (this) {
@@ -722,10 +727,6 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         // TODO: send interrupt signal
         
     }
-
-    
-    volatile boolean connecting = false;
-    volatile boolean connected = false;
     
     
     private boolean checkConnection(HttpChannel http) throws IOException {
index 3ceb5da..f4053a0 100644 (file)
@@ -156,14 +156,14 @@ public class BBuffer implements Cloneable, Serializable,
     
     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;
     }
@@ -500,6 +500,9 @@ public class BBuffer implements Cloneable, Serializable,
     }
     
     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
index 9b50f49..0b9a09e 100644 (file)
@@ -40,6 +40,10 @@ public class SocketConnector extends IOConnector {
         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.
@@ -109,7 +113,7 @@ public class SocketConnector extends IOConnector {
 
     static int id = 0;
 
-    synchronized NioThread getSelector() {
+    public synchronized NioThread getSelector() {
         if (selector == null) {
             String name = "SelectorThread-" + id++;
             selector = new NioThread(name, true);
index ec3f515..6c75d48 100644 (file)
@@ -142,6 +142,7 @@ public class SocketIOChannel extends IOChannel implements NioChannelCallback {
         // 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;
@@ -149,39 +150,36 @@ public class SocketIOChannel extends IOChannel implements NioChannelCallback {
                     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) {
index dac28eb..c45b211 100644 (file)
@@ -36,6 +36,7 @@ public class StaticContentService implements HttpService  {
     protected BBucket mb;
     
     protected boolean chunked = false;
+    int code = 200;
     
     protected String contentType = "text/plain";
 
@@ -43,6 +44,10 @@ public class StaticContentService implements HttpService  {
     public StaticContentService() {
     }
     
+    /**
+     * Used for testing chunked encoding.
+     * @return
+     */
     public StaticContentService chunked() {
       chunked = true;
       return this;
@@ -52,6 +57,11 @@ public class StaticContentService implements HttpService  {
         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];
@@ -97,19 +107,23 @@ public class StaticContentService implements HttpService  {
     @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
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java
deleted file mode 100644 (file)
index 284ee25..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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() {
-        
-    }
-}
index ee2b436..235d1e1 100644 (file)
@@ -40,10 +40,12 @@ import javax.servlet.SingleThreadModel;
 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;
 
@@ -136,6 +138,15 @@ public class ServletConfigImpl implements ServletConfig, HttpChannel.HttpService
         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
@@ -768,6 +779,11 @@ public class ServletConfigImpl implements ServletConfig, HttpChannel.HttpService
         ServletResponseImpl res = req.getResponse();
         
         // TODO
+        try {
+            instance.service(req, res);
+        } catch (ServletException e) {
+            throw new WrappedException(e);
+        }
     }
     
     /** Coyote / mapper adapter. Result of the mapper.
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JMXProxyServlet.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JMXProxyServlet.java
deleted file mode 100644 (file)
index 9eb2d84..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * 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;
-    }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JmxObjectManagerSpi.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JmxObjectManagerSpi.java
deleted file mode 100644 (file)
index 47578cc..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- */
-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;
-    }
-
-}
index 9cd3386..41126d8 100644 (file)
@@ -8,17 +8,24 @@ import java.io.InputStream;
 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;
@@ -27,6 +34,7 @@ import org.apache.tomcat.lite.io.SslConnector;
 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;
 
 /**
@@ -53,7 +61,9 @@ public class TestMain {
     private HttpConnector testProxy = new HttpConnector(serverCon);
 
     private HttpProxyService proxy;
-   
+
+    UJmxObjectManagerSpi jmx = new UJmxObjectManagerSpi();
+        
     public static TestMain shared() {
         if (defaultServer == null) {
             defaultServer = new TestMain();
@@ -70,7 +80,7 @@ public class 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()
@@ -80,6 +90,9 @@ public class TestMain {
                     "<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());
@@ -104,6 +117,9 @@ public class TestMain {
             }
         });
 
+        d.addWrapper(mCtx, "/ujmx", new UJmxHandler(jmx));
+        d.addWrapper(mCtx, "/jmx", 
+                new ServletConfigImpl(new JMXProxyServlet()));
     }
 
     public void run() {
@@ -114,9 +130,22 @@ public class TestMain {
         }
     } 
     
+    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);
@@ -156,7 +185,10 @@ public class TestMain {
 //            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() {
@@ -168,12 +200,63 @@ public class TestMain {
             }
         });
     }
+    
+    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(",");
@@ -185,24 +268,10 @@ public class TestMain {
             }
         }
 
-        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());
-            }
-        });
     }
     
 
@@ -251,7 +320,6 @@ public class TestMain {
         TestMain testMain = new TestMain();
         TestMain.defaultServer = testMain;
         testMain.om = new SimpleObjectManager(args);
-        testMain.initObjectManager("org/apache/tomcat/lite/test.properties");
         testMain.run();
         Main.waitStop();
     }
index 4487bce..1304038 100644 (file)
@@ -38,8 +38,8 @@ import org.apache.tomcat.lite.TestMain;
 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;
 
 /*
@@ -70,9 +70,20 @@ 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;
@@ -85,7 +96,7 @@ public class LiveHttpThreadedTest extends TestCase {
     
     public void test1000Async() throws Exception {
         try {
-            asyncRequest(10, 100, false);
+            asyncRequest(10, 100, false, clientCon);
         } finally {
             dumpHeap("heapAsync.bin");
         }
@@ -94,7 +105,7 @@ public class LiveHttpThreadedTest extends TestCase {
 
     public void test10000Async() throws Exception {
         try {
-            asyncRequest(20, 500, false);
+            asyncRequest(20, 500, false, clientCon);
         } finally {
             dumpHeap("heapAsyncBig.bin");
         }
@@ -102,7 +113,7 @@ public class LiveHttpThreadedTest extends TestCase {
 
     public void test1000AsyncSpdy() throws Exception {
         try {
-            asyncRequest(10, 100, true);
+            asyncRequest(10, 100, true, spdyClient);
         } finally {
             dumpHeap("heapSpdy1000.bin");
         }
@@ -111,14 +122,31 @@ public class LiveHttpThreadedTest extends TestCase {
 
     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) {
@@ -156,7 +184,7 @@ public class LiveHttpThreadedTest extends TestCase {
         urlRequest(10, 100);
     }
 
-    public void testURLRequest10000() throws Exception {
+    public void xtestURLRequest10000() throws Exception {
         urlRequest(20, 500);
 
     }
@@ -205,7 +233,9 @@ public class LiveHttpThreadedTest extends TestCase {
     // TODO: move to a servlet
     private void dumpHeap(String file) throws InstanceNotFoundException,
     MBeanException, ReflectionException, MalformedObjectNameException {
-
+        if (!dumpHeap) {
+            return;
+        }
         if (server == null) {
             server = ManagementFactory.getPlatformMBeanServer();
 
index 1c40a2d..1d21a57 100644 (file)
@@ -106,7 +106,7 @@ public class ProxyTest extends TestCase {
       String resStr = 
           TestMain.get("http://localhost:8903/chunked/test")
           .toString();
-      assertEquals(8, resStr.length());
+      assertEquals(4, resStr.length());
       assertTrue(resStr.indexOf("AAA") >= 0);
   }
   
index 8c60706..baade73 100644 (file)
@@ -1,12 +1,9 @@
-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