Removed the 'addons' package, moved the interfaces close to the packages that define...
authorcostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Thu, 26 Nov 2009 06:47:03 +0000 (06:47 +0000)
committercostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Thu, 26 Nov 2009 06:47:03 +0000 (06:47 +0000)
some code move around.

The web.xml parsing ( and some incipient 3.0 annotation processing ) is independent of tomcat, indended as
a user-library.

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

36 files changed:
modules/tomcat-lite/java/org/apache/tomcat/addons/Filesystem.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/addons/UserAuthentication.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/addons/UserSessionManager.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/addons/UserTemplateClassMapper.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ConfigLoader.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletContextConfig.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/AnnotationsProcessor.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/DomUtil.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WarDeploy.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WebXml.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/DefaultServlet.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/Dir2Html.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/Filesystem.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/LocalFilesystem.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JMXProxyServlet.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jmx/JmxObjectManagerSpi.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/BaseJspLoader.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/SimpleTemplateClassMapper.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/WildcardTemplateServlet.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JasperRuntime.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JspcServlet.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/AccessFilter.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/BasicAuthentication.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/FormAuthentication.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/UserAuthentication.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/session/RandomGenerator.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/session/UserSessionManager.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java [new file with mode: 0644]

diff --git a/modules/tomcat-lite/java/org/apache/tomcat/addons/Filesystem.java b/modules/tomcat-lite/java/org/apache/tomcat/addons/Filesystem.java
deleted file mode 100644 (file)
index 85c06a3..0000000
+++ /dev/null
@@ -1,41 +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.addons;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/** 
- * Abstract the filesystem - lighter than the JNDI used in catalina.
- * 
- * This can be used to port the File/Dav servlets to environments that 
- * don't have a file system access, or in servlet engines with class-based 
- * sandboxing.
- */
-public class Filesystem {
-
-    public OutputStream getOutputStream(String name) throws IOException {
-        return null;
-    }
-
-    public InputStream getInputStream(String name) throws IOException {
-        return new FileInputStream(name);
-    }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/addons/UserAuthentication.java b/modules/tomcat-lite/java/org/apache/tomcat/addons/UserAuthentication.java
deleted file mode 100644 (file)
index 88a0adc..0000000
+++ /dev/null
@@ -1,62 +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.addons;
-
-import java.io.IOException;
-import java.security.Principal;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Plugin for user auth.
- * 
- * This interface should support all common forms of auth, 
- * including Basic, Digest, Form and various other auth 
- * standards - the plugin has full control over request and
- * response.  
- * 
- * Container will verify the security constraints on URLs and 
- * call this for all URLs that have constraints. The plugin can
- * either authenticate and return the principal, or change 
- * the response - redirect, add headers, send content. 
- * 
- * Alternative: a simple Filter can do the same, with some conventions
- * to support it ( attributes ).
- * 
- * @author Costin Manolache
- */
-public interface UserAuthentication {
-
-    /**
-     * If req has all the info - return the principal.
-     * Otherwise set the challenge in response.
-     * 
-     * @param requestedMethod auth method from web.xml. Spec
-     *  complain plugins must support it. 
-     * @throws IOException 
-     */
-    public Principal authenticate(HttpServletRequest req, 
-                                  HttpServletResponse res, 
-                                  String requestedMethod) throws IOException;
-    
-    
-    public boolean isUserInRole(HttpServletRequest req,
-                                Principal p,
-                                String role);
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/addons/UserSessionManager.java b/modules/tomcat-lite/java/org/apache/tomcat/addons/UserSessionManager.java
deleted file mode 100644 (file)
index c450b5f..0000000
+++ /dev/null
@@ -1,60 +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.addons;
-
-import java.io.IOException;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpSession;
-
-/**
- * Session management plugin. No dependency on tomcat-lite, should
- * be possible to add this to tomcat-trunk or other containers.
- * 
- * The container will:
- * - extract the session id from request ( via a filter or built-ins )
- * - call this interface when the user makes the related calls in the 
- * servlet API.
- * - provide a context attribute 'context-listeners' with the 
- * List<EventListener> from web.xml 
- *
- * Implementation of this class must provide HttpSession object 
- * and implement the spec. 
- * 
- */
-public interface UserSessionManager {
-
-    
-    
-    HttpSession findSession(String requestedSessionId) throws IOException;
-
-    HttpSession createSession(String requestedSessionId);
-  
-    boolean isValid(HttpSession session);
-
-    void access(HttpSession session);
-  
-    void endAccess(HttpSession session);
-  
-  
-    void setSessionTimeout(int to);
-    
-    void setContext(ServletContext ctx);
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/addons/UserTemplateClassMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/addons/UserTemplateClassMapper.java
deleted file mode 100644 (file)
index 1a37cc7..0000000
+++ /dev/null
@@ -1,54 +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.addons;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
-/** 
- * Mapps a templating name ( like a jsp file ) to a class name 
- * generated by the template compiler.
- * 
- * This class is needed at runtime to support *.jsp mappings
- * or <jsp-file> - can be used to support other formats. 
- * 
- * The UserTemplateCompiler is only needed to support run-time
- * compilation - if ahead-of-time compilation is used 
- * all generated files can be mapped explicitly with  
- * <servlet><class-name>.
- * 
- * @author Costin Manolache
- */
-public interface UserTemplateClassMapper {
-
-    /**
-     * Generate and load the proxy corresponding to the template file.
-     *  
-     */
-    public Servlet loadProxy(String jspFile, 
-                             ServletContext ctx, 
-                             ServletConfig config) throws ServletException;
-
-    /**
-     * Quick check if the template ( or deps ) has been modified
-     * and the servlet needs to be regenerated;  
-     */
-    public boolean needsReload(String jspFile, Servlet s);
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ConfigLoader.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ConfigLoader.java
new file mode 100644 (file)
index 0000000..b356668
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ */
+package org.apache.tomcat.servlets.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+public class ConfigLoader {
+
+    public ServletContextConfig loadConfig(String basePath) {
+        
+        
+        String fileName = basePath + ServletContextConfig.SERIALIZED_PATH;
+        File f = new File(fileName);
+        if (f.exists()) {
+            ServletContextConfig contextConfig = new ServletContextConfig();
+            try {
+                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
+                contextConfig = (ServletContextConfig) ois.readObject();
+                return contextConfig;
+            } catch (Throwable e) {
+                System.err.println("Ignoring invalid .ser config " + e);
+                // ignore 
+            }
+        }
+        
+        return null;
+    }
+    
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletContextConfig.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletContextConfig.java
new file mode 100644 (file)
index 0000000..3b056e2
--- /dev/null
@@ -0,0 +1,164 @@
+/**
+ * 
+ */
+package org.apache.tomcat.servlets.config;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Struct representation of webapp configuration.
+ * 
+ * All the data in web.xml, annotations, etc should be represented 
+ * here. This class is serializable - but can be saved/loaded as 
+ * json or any 'pojo' persistence.
+ * 
+ * 
+ * Public fields to make it easy to access it, we can add accessors.
+ * Naming should match the web.xml element name.
+ * 
+ * @author Costin Manolache
+ */
+public class ServletContextConfig  implements Serializable {
+    
+    public static final String SERIALIZED_PATH = "/WEB-INF/deploy_web.ser";
+    private static final long serialVersionUID = 1728492145981883124L;
+    
+    public static final int CURRENT_VERSION = 1;
+    
+    public int version = CURRENT_VERSION;
+    
+    /**
+     * Main config ( web.xml ) path and timestamp - touch it to reload. 
+     */
+    public List<String> fileName = new ArrayList<String>();
+    public long timestamp;
+
+    public boolean full;
+    
+    public String displayName;
+
+    public HashMap<String, String> contextParam = new HashMap<String, String>();
+    
+    public HashMap<String, String> mimeMapping = new HashMap<String, String>(); // extension -> mime-type
+    
+    public ArrayList<String> listenerClass = new ArrayList<String>();
+    
+    public ArrayList<String> welcomeFileList = new ArrayList<String>();
+    
+    // code -> location 
+    public HashMap<String, String> errorPageCode= new HashMap<String, String>(); 
+    
+    // exception -> location
+    public HashMap<String, String> errorPageException= new HashMap<String, String>(); 
+
+    public HashMap<String, String> localeEncodingMapping= new HashMap<String, String>(); // locale -> encoding
+    
+    // public HashMap tagLibs; // uri->location
+    // jsp-property-group
+
+    // securityConstraint
+    public ArrayList<SecurityConstraintData> securityConstraint = new ArrayList<SecurityConstraintData>();
+    
+    // loginConfig
+    public String authMethod;
+    public String realmName;
+    public String formLoginPage;
+    public String formErrorPage;
+    
+    public ArrayList<String> securityRole = new ArrayList<String>();
+    
+    // envEntry
+    public ArrayList<EnvEntryData> envEntry = new ArrayList<EnvEntryData>();
+    
+    // ejbRef
+    // ejbLocalRef
+    // serviceRef
+    // resourceRef
+    // resourceEnvRef
+    // message-destination
+    // message-destinationRef
+    public HashMap<String, FilterData> filters = new HashMap<String, FilterData>();
+    public HashMap<String, ServletData> servlets = new HashMap<String, ServletData>();
+
+    public int sessionTimeout;
+    public boolean distributable;
+    
+    public HashMap<String, String> servletMapping = new HashMap<String, String>(); // url -> servlet
+    public ArrayList<FilterMappingData> filterMappings = new ArrayList<FilterMappingData>();
+    public boolean metadataComplete = false;
+    
+
+    // Normalized
+    public static class FilterMappingData implements Serializable {
+        private static final long serialVersionUID = -4533568066713041994L;
+        public String filterName;
+        
+        // Only one of the 2
+        public String urlPattern;
+        public String servletName;
+        
+        // REQUEST, FORWARD, INCLUDE, ERROR, ASYNC
+        public List<String> dispatcher = new ArrayList<String>();
+    }
+    
+    public static class EnvEntryData  implements Serializable {
+        private static final long serialVersionUID = 7023847615343715257L;
+        public String envEntryName;
+        public String envEntryType;
+        public String envEntryValue;
+    }
+    
+    public static class ServiceData implements Serializable {
+        public String name;
+        public String className;
+
+        public Map<String, String> initParams = new HashMap<String, String>();
+        
+        public boolean asyncSupported = false;
+    }
+    
+    public static class FilterData extends ServiceData implements Serializable {
+        private static final long serialVersionUID = -535820271746973166L;
+    }
+    
+    public static class ServletData extends ServiceData implements Serializable {
+        private static final long serialVersionUID = -3216904178501185930L;
+
+        public ServletData() {
+        }
+        public ServletData(String servletName, String servletClass) {
+            this.className = servletClass;
+            this.name = servletName;
+        }
+
+        public String jspFile;
+        public int loadOnStartup = -1;
+        public String runAs;
+        public Map<String, String> securityRoleRef = new HashMap<String, String>(); // roleName -> [roleLink]
+        public boolean multipartConfig = false;
+        
+        public List<String> declaresRoles = new ArrayList<String>();
+        
+    }
+    
+    public static class WebResourceCollectionData implements Serializable {
+        public String webResourceName;
+        public ArrayList<String> urlPattern = new ArrayList<String>();
+        public ArrayList<String> httpMethod = new ArrayList<String>();
+    }    
+    
+    public static class SecurityConstraintData  implements Serializable {
+        private static final long serialVersionUID = -4780214921810871769L;
+
+        public ArrayList<String> roleName = new ArrayList<String>(); //   auth-constraint/role
+
+        public ArrayList<WebResourceCollectionData> webResourceCollection = 
+            new ArrayList<WebResourceCollectionData>();
+        public String transportGuarantee;
+        
+    }    
+}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/AnnotationsProcessor.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/AnnotationsProcessor.java
new file mode 100644 (file)
index 0000000..86b0e4d
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ */
+package org.apache.tomcat.servlets.config.deploy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.tomcat.servlets.config.ServletContextConfig;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterMappingData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.ServiceData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+
+// TODO: move to 'tools' dir, don't include in runtime
+// ( same for xml processor ) - use binary 
+// 
+
+// TODO: stupid ordering rules - probably will require merging configs
+// and other ugliness.
+
+// TODO: the even more brain-damaged ServletContextInitializer and 
+// HandlesTypes - which requires recording all annotations for all classes,
+// and worse - all the hierarchy to detect annotations on super.
+
+/**
+ * Post-compile or deploy tool: will scan classes and jars and 
+ * generate an annotation file.
+ * 
+ * Will process:
+ *  - annotations - for each class
+ *  - find tld descriptors
+ *  - web.xml and fragments
+ *  
+ * Output: a .ser file, for faster tomcat startup and a 'compete'
+ * web.xml file.  
+ * 
+ * Tomcat should not read all classes each time it starts, or 
+ * depend on bcel at runtime.
+ * 
+ * Servlet spec makes the worst use of annotations by requiring 
+ * scanning all classes. This should be a compile-time tool only !
+ * 
+ * @author Costin Manolache
+ */
+public class AnnotationsProcessor {
+
+    String baseDir;
+    ServletContextConfig cfg;
+
+    
+    
+    public AnnotationsProcessor(ServletContextConfig cfg2) {
+        this.cfg = cfg2;
+    }
+
+    public void processWebapp(String baseN) throws IOException {
+        if (!baseN.endsWith("/")) {
+            baseN = baseN + "/";
+        }
+        processDir(baseN + "classes");
+        
+        File lib = new File(baseN + "lib");
+        if (!lib.isDirectory()) {
+            return;
+        }
+        File[] files = lib.listFiles();
+        if (files == null) {
+            return;
+        }
+        
+        for (File f: files) {
+            if (!f.isDirectory() && f.getName().endsWith(".jar")) {
+                processJar(f.getCanonicalPath());
+            }
+        }        
+    }
+    
+    public void processJar(String path) throws IOException {
+        JarFile jar = new JarFile(path);
+        Enumeration<JarEntry> entries = jar.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry entry = entries.nextElement();
+            String name = entry.getName();
+            if (name.endsWith(".class")) {
+                processClass(jar.getInputStream(entry),
+                        "", name);
+            } else if (name.equals("META-INF/services/javax.servlet.ServletContainerInitializer")) {
+                
+            }
+            
+        }
+    }
+
+    public void processDir(String base) throws IOException {
+        // TODO: keep track of files to avoid loops
+        processDir(new File(base));
+    }
+    
+    public void processDir(File base) throws IOException {
+        if (!base.isDirectory()) {
+            return;
+        }
+        String baseN = base.getCanonicalPath();
+        if (!baseN.endsWith("/")) {
+            baseN = baseN + "/";
+        }
+        
+        File[] files = base.listFiles();
+        if (files != null) {
+            for (File f: files) {
+                if (f.isDirectory()) {
+                    System.err.println(f);
+                    processDir(f);
+                } else if (f.getName().endsWith(".class")) {
+                    try {
+                        processClass(new FileInputStream(f), base.getCanonicalPath(),
+                                f.getCanonicalPath());
+                    } catch (IOException e) {
+                        // TODO Auto-generated catch block
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+        
+    }
+    
+    private static Map<String, Object> asmList2Map(List list) {
+        Map<String, Object> values = new HashMap();
+        for (int i = 0; i < list.size(); i+= 2) {
+            String name = (String) list.get(i);
+            Object val = list.get(i + 1);
+            values.put(name, val);
+        }
+        return values;
+    }
+    
+    private static Map<String, AnnotationNode> annotationMap(List annL) {
+        Map<String, AnnotationNode> values = new HashMap();
+        if (annL != null) {
+            for (Object annO: annL) {
+                AnnotationNode ann = (AnnotationNode) annO;
+                String name = Type.getType(ann.desc).toString();
+                values.put(name, ann);
+            }
+        }
+        return values;
+    }
+    
+    
+    
+    public void processClass(InputStream classStream, 
+                             String base,
+                             String classFile) throws IOException {
+        String classPath = classFile.substring(base.length() + 1);
+        classPath = classPath.substring(0, classPath.length() - ".class".length());
+        classPath = classPath.replace("/", ".");
+        
+        ClassReader classReader = new ClassReader(classStream);
+        ClassNode cN = new ClassNode();
+        classReader.accept(cN, 
+                ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES);
+
+        String className = cN.name;
+        
+        Map<String, AnnotationNode> annotations = annotationMap(cN.visibleAnnotations);
+
+        processServlets(className, annotations, cN);
+        processWebFilter(className, annotations);
+        
+        AnnotationNode listenerA = annotations.get("javax.servlet.annotation.WebListener");
+        if (listenerA != null) {
+            // TODO: checks
+            cfg.listenerClass.add(className);
+        }
+        
+        
+//        for (AnnotationNode mN : annotations.values()) {
+//            String ann = Type.getType(mN.desc).toString();
+//            Map<String, Object> values = asmList2Map(mN.values); 
+//            
+//            if ("javax.servlet.annotation.HandlesTypes".equals(ann)) {
+//            } else if ("javax.servlet.annotation.MultipartConfig".equals(ann)) {
+//            } else if ("javax.annotation.security.RunAs".equals(ann)) {
+//            } else if ("javax.annotation.security.DeclareRoles".equals(ann)) {
+//            } else if ("javax.annotation.security.RolesAllowed".equals(ann)) {
+//            } else if ("javax.annotation.security.DenyAll".equals(ann)) {
+//            } else if ("javax.annotation.security.PermitAll".equals(ann)) {
+//            } else if ("javax.servlet.annotation.WebFilter".equals(ann)) {
+//            } else if ("javax.servlet.annotation.WebServlet".equals(ann)) {
+//                // in WebServlet
+//            } else if ("javax.servlet.annotation.WebListener".equals(ann)) {
+//            } else if ("javax.servlet.annotation.WebInitParam".equals(ann)) {
+//                // In WebServlet, (WebFilter)
+//            } else {
+//                System.err.println("\n" + className + " " + Type.getType(mN.desc));
+//            }
+//        }
+
+    }
+
+    private void processServlets(String className,
+            Map<String, AnnotationNode> annotations, ClassNode cn) {
+        
+        AnnotationNode webServletA = 
+            annotations.get("javax.servlet.annotation.WebServlet");
+        if (webServletA != null) {
+            ServletData sd = new ServletData();
+            // TODO: validity checks (implements servlet, etc)
+            Map<String, Object> params = asmList2Map(webServletA.values); 
+            
+            processService(className, webServletA,
+                    sd, params);
+            
+            if (params.containsKey("loadOnStartup")) {
+                sd.loadOnStartup = (Integer)params.get("loadOnStartup");
+            }
+            if (annotations.get("javax.servlet.annotation.MultipartConfig") != null) {
+                sd.multipartConfig = true;
+            }
+            
+            AnnotationNode declareA = annotations.get("javax.annotation.security.DeclareRoles");
+            if (declareA != null) {
+                Map<String, Object> runAsParams = asmList2Map(declareA.values);
+                ArrayList roles = (ArrayList) runAsParams.get("value");
+                for (Object r: roles) {
+                    sd.declaresRoles.add((String) r);
+                }
+            }
+            
+            AnnotationNode runAsA = annotations.get("javax.annotation.security.RunAs");
+            if (runAsA != null) {
+                Map<String, Object> runAsParams = asmList2Map(runAsA.values);
+                
+                sd.runAs = (String) runAsParams.get("value");
+            }
+            
+            cfg.servlets.put(sd.name, sd);
+
+            ArrayList urls = (ArrayList) params.get("urlPatterns");
+            if (urls == null) {
+                urls = (ArrayList) params.get("value");
+            }
+
+            for (Object urlO: urls) {
+                cfg.servletMapping.put((String) urlO, 
+                        sd.name);
+            }
+
+            // TODO: collect them, add on each of the URLs
+            // TODO: also on methods
+            AnnotationNode rolesA = annotations.get("javax.annotation.security.RolesAllowed");
+            if (rolesA != null) {
+                
+            }            
+            for (Object o: cn.methods) {
+                MethodNode methodNode = (MethodNode) o;
+                System.err.println(methodNode.desc);
+            }
+            
+            
+            
+        }
+    }
+
+    private void processWebFilter(String className,
+            Map<String, AnnotationNode> annotations) {
+        AnnotationNode webFilterA = annotations.get("javax.servlet.annotation.WebServlet");
+        if (webFilterA != null) {
+            // TODO: validity checks (implements servlet, etc)
+            
+            FilterData sd = new FilterData();
+            Map<String, Object> params = asmList2Map(webFilterA.values); 
+            
+            processService(className, webFilterA, sd, params);
+
+            if (params.containsKey("asyncSupported")) {
+                sd.asyncSupported = (Boolean) params.get("asyncSupported");
+            }
+            
+            cfg.filters.put(sd.name, sd);
+            
+            ArrayList urls = (ArrayList) params.get("urlPatterns");
+            if (urls == null) {
+                urls = (ArrayList) params.get("value");
+            }
+            for (Object urlO: urls) {
+                FilterMappingData fmap = new FilterMappingData();
+                fmap.filterName = sd.name;
+                fmap.urlPattern = (String) urlO;
+                
+                cfg.filterMappings.add(fmap);
+            }
+        }
+    }
+
+    private ServiceData processService(String className,
+            AnnotationNode webServletA, ServiceData sd, Map<String, Object> params) {
+        
+        sd.name = (String) params.get("name");
+        if (sd.name == null) {
+            sd.name = className;
+        }
+        sd.className = className;
+        
+        if (params.containsKey("initParams")) {
+            ArrayList initParamL = (ArrayList) params.get("initParams");
+            for (Object initParamO: initParamL) {
+                AnnotationNode initParamA = (AnnotationNode) initParamO;
+                
+                Map<String, Object> initParams = asmList2Map(initParamA.values);
+                sd.initParams.put((String) initParams.get("name"),
+                        (String) initParams.get("value"));
+            }
+        }
+        
+        if (params.containsKey("asyncSupported")) {
+            sd.asyncSupported = (Boolean) params.get("asyncSupported");
+        }
+        return sd;
+    }
+    
+    
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/DomUtil.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/DomUtil.java
new file mode 100644 (file)
index 0000000..64e2870
--- /dev/null
@@ -0,0 +1,240 @@
+package org.apache.tomcat.servlets.config.deploy;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ *  Few simple utils to read DOM
+ *
+ * @author Costin Manolache
+ */
+public class DomUtil {
+    private static Logger log=
+        Logger.getLogger( DomUtil.class.getName() );
+
+    // -------------------- DOM utils --------------------
+
+    /** Get the trimed text content of a node or null if there is no text
+     */
+    public static String getContent(Node n ) {
+        if( n==null ) return null;
+        Node n1=DomUtil.getChild(n, Node.TEXT_NODE);
+
+        if( n1==null ) return null;
+
+        String s1=n1.getNodeValue();
+        return s1.trim();
+    }
+
+    /** Get the first element child.
+     * @param parent lookup direct childs
+     * @param name name of the element. If null return the first element.
+     */
+    public static Node getChild( Node parent, String name ) {
+        if( parent==null ) return null;
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( node.getNodeType()!=Node.ELEMENT_NODE)
+                continue;
+            if( name != null &&
+                name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+            if( name == null ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public static String getAttribute(Node element, String attName ) {
+        NamedNodeMap attrs=element.getAttributes();
+        if( attrs==null ) return null;
+        Node attN=attrs.getNamedItem(attName);
+        if( attN==null ) return null;
+        return attN.getNodeValue();
+    }
+
+    public static void setAttribute(Node node, String attName, String val) {
+        NamedNodeMap attributes=node.getAttributes();
+        Node attNode=node.getOwnerDocument().createAttribute(attName);
+        attNode.setNodeValue( val );
+        attributes.setNamedItem(attNode);
+    }
+    
+    public static void removeAttribute( Node node, String attName ) {
+        NamedNodeMap attributes=node.getAttributes();
+        attributes.removeNamedItem(attName);                
+    }
+    
+    
+    /** Set or replace the text value 
+     */ 
+    public static void setText(Node node, String val) {
+        Node chld=DomUtil.getChild(node, Node.TEXT_NODE);
+        if( chld == null ) {
+            Node textN=node.getOwnerDocument().createTextNode(val);
+            node.appendChild(textN);
+            return;
+        }
+        // change the value
+        chld.setNodeValue(val);           
+    }
+
+    /** Find the first direct child with a given attribute.
+     * @param parent
+     * @param elemName name of the element, or null for any 
+     * @param attName attribute we're looking for
+     * @param attVal attribute value or null if we just want any
+     */ 
+    public static Node findChildWithAtt(Node parent, String elemName,
+                                        String attName, String attVal) {
+        
+        Node child=DomUtil.getChild(parent, Node.ELEMENT_NODE);
+        if( attVal== null ) {
+            while( child!= null &&
+                    ( elemName==null || elemName.equals( child.getNodeName())) && 
+                    DomUtil.getAttribute(child, attName) != null ) {
+                child=getNext(child, elemName, Node.ELEMENT_NODE );
+            }
+        } else {
+            while( child!= null && 
+                    ( elemName==null || elemName.equals( child.getNodeName())) && 
+                    ! attVal.equals( DomUtil.getAttribute(child, attName)) ) {
+                child=getNext(child, elemName, Node.ELEMENT_NODE );
+            }
+        }
+        return child;        
+    }    
+    
+
+    /** Get the first child's content ( ie it's included TEXT node ).
+     */
+    public static String getChildContent( Node parent, String name ) {
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return getContent( node );
+            }
+        }
+        return null;
+    }
+
+    /** Get the first direct child with a given type
+     */
+    public static Node getChild( Node parent, int type ) {
+        Node n=parent.getFirstChild();
+        while( n!=null && type != n.getNodeType() ) {
+            n=n.getNextSibling();
+        }
+        if( n==null ) return null;
+        return n;
+    }
+
+    /** Get the next sibling with the same name and type
+     */
+    public static Node getNext( Node current ) {
+        String name=current.getNodeName();
+        int type=current.getNodeType();
+        return getNext( current, name, type);
+    }
+
+    /** Return the next sibling with a given name and type
+     */ 
+    public static Node getNext( Node current, String name, int type) {
+        Node first=current.getNextSibling();
+        if( first==null ) return null;
+
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            
+            if( type >= 0 && node.getNodeType() != type ) continue;
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name==null )
+                return node;
+            if( name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public static class NullResolver implements EntityResolver {
+        public InputSource resolveEntity (String publicId,
+                                                   String systemId)
+            throws SAXException, IOException
+        {
+//            if( log.isTraceEnabled())
+//                log.trace("ResolveEntity: " + publicId + " " + systemId);
+            return new InputSource(new StringReader(""));
+        }
+    }
+
+//    public static void setAttributes( Object o, Node parent)
+//    {
+//        NamedNodeMap attrs=parent.getAttributes();
+//        if( attrs==null ) return;
+//
+//        for (int i=0; i<attrs.getLength(); i++ ) {
+//            Node n=attrs.item(i);
+//            String name=n.getNodeName();
+//            String value=n.getNodeValue();
+//
+////            if( log.isTraceEnabled() )
+////                log.trace("Attribute " + parent.getNodeName() + " " +
+////                            name + "=" + value);
+//            try {
+//                IntrospectionUtils.setProperty(o, name, value);
+//            } catch( Exception ex ) {
+//                ex.printStackTrace();
+//            }
+//        }
+//    }
+
+    /** Read XML as DOM.
+     */
+    public static Document readXml(InputStream is)
+        throws SAXException, IOException, ParserConfigurationException
+    {
+        DocumentBuilderFactory dbf =
+            DocumentBuilderFactory.newInstance();
+
+        dbf.setValidating(false);
+        dbf.setIgnoringComments(false);
+        dbf.setIgnoringElementContentWhitespace(true);
+        //dbf.setCoalescing(true);
+        //dbf.setExpandEntityReferences(true);
+
+        DocumentBuilder db = null;
+        db = dbf.newDocumentBuilder();
+        db.setEntityResolver( new NullResolver() );
+
+        // db.setErrorHandler( new MyErrorHandler());
+
+        Document doc = db.parse(is);
+        return doc;
+    }
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WarDeploy.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WarDeploy.java
new file mode 100644 (file)
index 0000000..8edc547
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ */
+package org.apache.tomcat.servlets.config.deploy;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+import org.apache.tomcat.servlets.config.ConfigLoader;
+import org.apache.tomcat.servlets.config.ServletContextConfig;
+
+/**
+ * Independent of tomcat-lite - will read the current context config, 
+ * parse classes/jars for annotations - and generate a .ser file 
+ * containing all info. 
+ * 
+ * This can be used to display informations about the config ( in a 
+ * container-independent servlet ), or by the container for faster 
+ * load times. 
+ *  
+ * @author Costin Manolache
+ */
+public class WarDeploy extends ConfigLoader implements Runnable {
+
+    public ServletContextConfig loadConfig(String basePath) {
+        ServletContextConfig contextConfig = super.loadConfig(basePath);
+
+        boolean needsDeploy = contextConfig == null;
+        
+        if (contextConfig != null) {
+            if (contextConfig.version != ServletContextConfig.CURRENT_VERSION) {
+                needsDeploy = true;
+            } else {
+                // Check web.xml and other dep file timestamp(s)
+                for (String fn : contextConfig.fileName) {
+                    File f = new File(fn);
+                    if (f.lastModified() > contextConfig.timestamp) {
+                        needsDeploy = true;
+                        break;
+                    }
+                }
+            }
+        }
+        
+        if (needsDeploy) {
+            setBase(basePath);
+            run();
+            contextConfig = super.loadConfig(basePath);
+        }
+
+        return contextConfig;
+    }
+    
+    @Override
+    public void run() {
+        if (base == null) {
+            return; // nothing we can do
+        }
+        
+        ServletContextConfig contextConfig = new ServletContextConfig();
+        contextConfig.timestamp = System.currentTimeMillis();
+
+        File webXmlF = new File(base + "/WEB-INF/web.xml");
+        boolean needsAnnotations = true;
+        
+        if (webXmlF.exists()) {
+            WebXml webXml = new WebXml(contextConfig);
+            try {
+                webXml.readWebXml(base + "/WEB-INF/web.xml");
+                if (contextConfig.metadataComplete) {
+                    needsAnnotations = false;
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        
+        try {
+        if (needsAnnotations) {
+            AnnotationsProcessor ap = new AnnotationsProcessor(contextConfig);
+            ap.processWebapp(base);
+        }
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+
+
+        // Save
+        try {
+            ObjectOutputStream ois = 
+                new ObjectOutputStream(new FileOutputStream(base + 
+                        ServletContextConfig.SERIALIZED_PATH));
+            ois.writeObject(contextConfig);
+            ois.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        
+    }
+
+    String base;
+    
+
+    public static void main(String[] args) {
+        String base = args[0];
+        WarDeploy wd = new WarDeploy();
+        wd.setBase(base);
+        wd.run();
+    }
+
+    private void setBase(String base) {
+        this.base = base;
+    }
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WebXml.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/config/deploy/WebXml.java
new file mode 100644 (file)
index 0000000..df90869
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ */
+package org.apache.tomcat.servlets.config.deploy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.servlet.ServletException;
+
+import org.apache.tomcat.servlets.config.ServletContextConfig;
+import org.apache.tomcat.servlets.config.ServletContextConfig.EnvEntryData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterMappingData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.SecurityConstraintData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.WebResourceCollectionData;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/** 
+ * General-purpose utility to process an web.xml file. Result
+ *  is a tree of objects starting with WebAppData.
+ * 
+ * TODO: allow writting of web.xml, allow modification ( preserving 
+ * comments )
+ * 
+ * @author costin
+ */
+public class WebXml {
+    ServletContextConfig d;
+    
+    public WebXml(ServletContextConfig cfg) {
+        d = cfg;
+    }
+
+    public void processJar(String path) throws IOException {
+        JarFile jar = new JarFile(path);
+        Enumeration<JarEntry> entries = jar.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry entry = entries.nextElement();
+            String name = entry.getName();
+            if (name.equals("META-INF/web-fragment.xml")) {
+                processFragment(jar.getInputStream(entry));
+            }
+        }
+    }
+    
+    private void processFragment(InputStream inputStream) throws IOException {
+        readWebXml(inputStream, "web-fragment");
+        
+    }
+
+    public void readWebXml(String baseDir) throws IOException {
+        readWebXml(baseDir, "web-app");
+    }
+     
+    public void readWebXml(String baseDir, String topName) throws IOException {
+        try {
+            File webXmlFile = new File(baseDir);
+            if (!webXmlFile.exists()) {
+                return;
+            }
+            d.fileName.add(webXmlFile.getCanonicalPath());
+            
+            FileInputStream fileInputStream = new FileInputStream(webXmlFile);
+            readWebXml(fileInputStream, topName);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new IOException(e.getMessage());
+        }
+    }
+    
+     public void readWebXml(InputStream fileInputStream, String topName) 
+             throws IOException {
+         try {
+                    
+            Document document = DomUtil.readXml(fileInputStream);
+            Node webappNode = DomUtil.getChild(document, topName);
+
+            String fullS = DomUtil.getAttribute(webappNode, "full");
+            if (fullS != null && fullS.equalsIgnoreCase("true")) {
+                d.full = true;
+            }
+            String metaCompleteS = DomUtil.getAttribute(webappNode, "full");
+            if ("true".equalsIgnoreCase(metaCompleteS)) {
+                d.metadataComplete = true;
+            }
+            
+            d.displayName = DomUtil.getAttribute(webappNode, "display-name");
+                        
+            // Process each child of web-app
+            Node confNode = DomUtil.getChild(webappNode, "filter");
+            while (confNode != null ) {
+                processFilter(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "filter-mapping");
+            while (confNode != null ) {
+                processFilterMapping(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "context-param");
+            while (confNode != null ) {
+                String n = DomUtil.getChildContent(confNode, "param-name").trim();
+                String v = DomUtil.getChildContent(confNode, "param-value").trim();
+                d.contextParam.put(n, v);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "mime-mapping");
+            while (confNode != null ) {
+                String n = DomUtil.getChildContent(confNode, "extension");
+                String t = DomUtil.getChildContent(confNode, "mime-type");
+                d.mimeMapping.put(n, t);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "error-page");
+            while (confNode != null ) {
+                processErrorPage(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "jsp-config");
+            while (confNode != null ) {
+                processJspConfig(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "servlet");
+            while (confNode != null ) {
+                processServlet(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "servlet-mapping");
+            while (confNode != null ) {
+                processServletMapping(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "listener");
+            while (confNode != null ) {
+                String lClass = DomUtil.getChildContent(confNode, "listener-class");
+                d.listenerClass.add(lClass);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "security-constraint");
+            while (confNode != null ) {
+                processSecurityConstraint(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "login-config");
+            while (confNode != null ) {
+                processLoginConfig(confNode);
+                confNode = DomUtil.getNext(confNode);
+                if (confNode != null) 
+                    throw new ServletException("Multiple login-config");
+            }
+
+            confNode = DomUtil.getChild(webappNode, "session-config");
+            while (confNode != null ) {
+                String n = DomUtil.getChildContent(confNode, "session-timeout");
+                int stout = Integer.parseInt(n);
+                d.sessionTimeout = stout;
+                confNode = DomUtil.getNext(confNode);
+                if (confNode != null) 
+                    throw new ServletException("Multiple session-config");
+            }
+
+            confNode = DomUtil.getChild(webappNode, "welcome-file-list");
+            while (confNode != null ) {
+                Node wf = DomUtil.getChild(confNode, "welcome-file");
+                while (wf != null) {
+                    String file = DomUtil.getContent(wf);
+                    d.welcomeFileList.add(file);
+                    wf = DomUtil.getNext(wf);
+                }
+                // more sections ?
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            // Not supported right now - TODO: collect, have jndi plugin
+            confNode = DomUtil.getChild(webappNode, "env-entry");
+            while (confNode != null ) {
+                processEnvEntry(confNode);
+                confNode = DomUtil.getNext(confNode);
+            }
+            
+            confNode = DomUtil.getChild(webappNode, "locale-encoding-mapping-list");
+            while (confNode != null ) {
+                confNode = DomUtil.getNext(confNode);
+                String n = DomUtil.getChildContent(confNode, "locale");
+                String t = DomUtil.getChildContent(confNode, "encoding");
+                d.localeEncodingMapping.put(n, t);
+            }
+
+            confNode = DomUtil.getChild(webappNode, "distributable");
+            while (confNode != null ) {
+                d.distributable = true;
+                confNode = DomUtil.getNext(confNode);
+            }
+
+            confNode = DomUtil.getChild(confNode, "security-role");
+            while (confNode != null ) {
+                String n = DomUtil.getChildContent(confNode, "role-name");
+                d.securityRole.add(n);
+                confNode = DomUtil.getNext(confNode);
+            }
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    throw new IOException(e.getMessage());
+                }
+    }
+    
+    private void processJspConfig(Node confNode) {
+        Node tagLib = DomUtil.getChild(confNode, "taglib");
+        while (tagLib != null) {
+            String uri = DomUtil.getChildContent(tagLib, "taglib-uri");
+            String l = DomUtil.getChildContent(tagLib, "taglib-location");
+            //d.tagLibs.put(uri, l);
+            tagLib = DomUtil.getNext(tagLib);
+        }
+        
+        tagLib = DomUtil.getChild(confNode, "jsp-property-group");
+        while (tagLib != null) {
+            // That would be the job of the JSP servlet to process.
+            tagLib = DomUtil.getNext(tagLib);
+        }
+    }
+
+    private void processEnvEntry(Node confNode) {
+        EnvEntryData ed = new EnvEntryData();
+        ed.envEntryName = DomUtil.getChildContent(confNode,"env-entry-name");
+        ed.envEntryType = DomUtil.getChildContent(confNode,"env-entry-type");
+        ed.envEntryValue = DomUtil.getChildContent(confNode,"env-entry-value");
+        d.envEntry.add(ed);
+    }
+
+    private void processLoginConfig(Node confNode) {
+        d.authMethod = DomUtil.getChildContent(confNode,"auth-method");
+        d.realmName = DomUtil.getChildContent(confNode,"auth-method");
+        Node formNode = DomUtil.getChild(confNode, "form-login-config");
+        if (formNode != null) {
+            d.formLoginPage = DomUtil.getChildContent(formNode,"form-login-page");
+            d.formErrorPage = DomUtil.getChildContent(formNode,"form-error-page");
+        }
+    }
+
+    private void processSecurityConstraint(Node confNode) {
+        SecurityConstraintData sd = new SecurityConstraintData();
+        Node cn = DomUtil.getChild(confNode, "web-resource-collection");
+        while (cn != null) {
+            WebResourceCollectionData wrd = new WebResourceCollectionData();
+            wrd.webResourceName = DomUtil.getChildContent(cn, "web-resource-name");
+            Node scn = DomUtil.getChild(cn,"url-pattern");
+            while (scn != null) {
+                wrd.urlPattern.add(DomUtil.getContent(scn));
+                scn = DomUtil.getNext(scn);
+            }
+            scn = DomUtil.getChild(cn,"http-method");
+            while (scn != null) {
+                wrd.httpMethod.add(DomUtil.getContent(scn));
+                scn = DomUtil.getNext(scn);
+            }
+            cn = DomUtil.getNext(cn);
+            sd.webResourceCollection.add(wrd);
+        }
+        
+        d.securityConstraint.add(sd);
+    }
+
+    private void processErrorPage(Node confNode) {
+        String name = DomUtil.getChildContent(confNode,"location");
+        String c = DomUtil.getChildContent(confNode,"error-code");
+        String t = DomUtil.getChildContent(confNode,"exception-type");
+        if (c != null) {
+            d.errorPageCode.put(c, name);
+        }
+        if (t != null) {
+            d.errorPageException.put(t, name);
+        }
+    }
+
+    private void processServlet(Node confNode) throws ServletException {
+        ServletData sd = new ServletData();
+
+        sd.name = DomUtil.getChildContent(confNode,"servlet-name");
+        sd.className = DomUtil.getChildContent(confNode,"servlet-class");
+        sd.jspFile = DomUtil.getChildContent(confNode,"jsp-file");
+        
+        processInitParams(confNode, sd.initParams);
+        
+        d.servlets.put( sd.name, sd );
+        
+        String los = DomUtil.getChildContent(confNode, "load-on-startup");
+        if (los != null ) { 
+            sd.loadOnStartup = Integer.parseInt(los);
+        }
+        
+        Node sn = DomUtil.getChild(confNode, "security-role-ref");
+        while (sn != null ) {
+            String roleName = DomUtil.getChildContent(sn, "role-name");
+            String roleLink = DomUtil.getChildContent(sn, "role-link");
+            if (roleLink == null) {
+                sd.securityRoleRef.put(roleName, "");
+            } else {
+                sd.securityRoleRef.put(roleName, roleLink);
+            }
+            sn = DomUtil.getNext(sn);
+        }
+    }
+
+    private void processInitParams(Node confNode, Map initParams) {
+        Node initN = DomUtil.getChild(confNode, "init-param");
+        while (initN != null ) {
+            String n = DomUtil.getChildContent(initN, "param-name");
+            String v = DomUtil.getChildContent(initN, "param-value");
+            initParams.put(n, v);
+            initN = DomUtil.getNext(initN);
+        }
+    }
+
+    private void processServletMapping(Node confNode) {
+        String name = DomUtil.getChildContent(confNode,"servlet-name");
+        Node dataN = DomUtil.getChild(confNode, "url-pattern");
+        while (dataN != null) {
+            String path = DomUtil.getContent(dataN).trim();
+            dataN = DomUtil.getNext(dataN);
+            
+            if (! (path.startsWith("/") || path.startsWith("*"))) {
+                // backward compat 
+                path = "/" + path;
+            }
+            d.servletMapping.put(path, name);
+        }
+    }
+
+    private void processFilterMapping(Node confNode) {
+      String filterName = DomUtil.getChildContent(confNode,"filter-name");
+      // multiple 
+      ArrayList dispatchers = new ArrayList();
+      Node dataN = DomUtil.getChild(confNode, "dispatcher");
+      while (dataN != null ) {
+          String d = DomUtil.getContent(dataN);
+          dispatchers.add(d);
+          dataN = DomUtil.getNext(dataN);
+      }
+      
+      // Multiple url-pattern and servlet-name in one
+      // mapping rule. Need to be applied in order.
+      dataN = DomUtil.getChild(confNode, "url-pattern");
+      while (dataN != null ) {
+        FilterMappingData fm = new FilterMappingData();
+        fm.filterName = filterName;
+        fm.dispatcher = dispatchers;
+        String path = DomUtil.getContent(dataN);
+        dataN = DomUtil.getNext(dataN);
+        fm.urlPattern = path;
+        d.filterMappings.add(fm);
+      }
+      dataN = DomUtil.getChild(confNode, "servlet-name");
+      while (dataN != null ) {
+        FilterMappingData fm = new FilterMappingData();
+        fm.filterName = filterName;
+        fm.dispatcher = dispatchers;
+        String sn = DomUtil.getContent(dataN);
+        dataN = DomUtil.getNext(dataN);
+        fm.servletName = sn;
+        d.filterMappings.add(fm);
+      }
+    }
+
+    private void processFilter(Node confNode) {
+        String name = DomUtil.getChildContent(confNode,"filter-name");
+        String sclass = DomUtil.getChildContent(confNode,"filter-class");
+        
+        FilterData fd = new FilterData();
+        processInitParams(confNode, fd.initParams);
+        fd.name = name;
+        fd.className = sclass;
+        d.filters.put(name, fd);
+    }
+    
+}
index 2200694..e2ed865 100644 (file)
@@ -40,11 +40,8 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
-import org.apache.tomcat.addons.Filesystem;
-import org.apache.tomcat.integration.ObjectManager;
-import org.apache.tomcat.integration.simple.ServletHelper;
 import org.apache.tomcat.servlets.util.Range;
+import org.apache.tomcat.servlets.util.URLEncoder;
 
 /**
  * The default resource-serving servlet for most web applications,
@@ -126,8 +123,6 @@ public class DefaultServlet  extends HttpServlet {
 
 
     // --------------------------------------------------------- Public Methods
-
-    protected ObjectManager om;
     protected Filesystem fs;
 
     /**
@@ -135,14 +130,15 @@ public class DefaultServlet  extends HttpServlet {
      */
     public void destroy() {
     }
-
-
+    
     /**
      * Initialize this servlet.
      */
     public void init() throws ServletException {
-        om = ServletHelper.getObjectManager(getServletContext());
-        fs = (Filesystem) om.get(Filesystem.class.getName());
+        if (fs == null) {
+            // R/O - no write
+            fs = new Filesystem();
+        }
 
         String realPath = getServletContext().getRealPath("/");
         basePath = new File(realPath);
@@ -517,9 +513,8 @@ public class DefaultServlet  extends HttpServlet {
                 && (request.getHeader("Range") == null) )
                 || (ranges == FULL) ) {
 
-            processFullFile(response, content, resFile, contentType, 
-                            contentLength, ostream, writer);
-
+            processSingleRange(response, content, resFile, contentType, 
+                    ostream, writer, ranges, contentLength);
         } else {
 
             if ((ranges == null) || (ranges.isEmpty()))
@@ -531,7 +526,7 @@ public class DefaultServlet  extends HttpServlet {
             if (ranges.size() == 1) {
 
                 processSingleRange(response, content, resFile, contentType, 
-                                   ostream, writer, ranges);
+                        ostream, writer, ranges, contentLength);
 
             } else {
 
@@ -572,13 +567,19 @@ public class DefaultServlet  extends HttpServlet {
     }
 
 
-    private void processSingleRange(HttpServletResponse response, boolean content, File resFile, String contentType, ServletOutputStream ostream, PrintWriter writer, ArrayList ranges) throws IOException {
-        Range range = (Range) ranges.get(0);
-        response.addHeader("Content-Range", "bytes "
-                           + range.start
-                           + "-" + range.end + "/"
-                           + range.length);
-        long length = range.end - range.start + 1;
+    private void processSingleRange(HttpServletResponse response, boolean content, File resFile, String contentType, ServletOutputStream ostream, PrintWriter writer, ArrayList ranges,
+            long contentLength) throws IOException {
+        Range range = null;
+        long length = contentLength;
+        
+        if (ranges != null && ranges.size() > 0) {
+            range = (Range) ranges.get(0);
+            response.addHeader("Content-Range", "bytes "
+                    + range.start
+                    + "-" + range.end + "/"
+                    + range.length);
+            length = range.end - range.start + 1;
+        }
         if (length < Integer.MAX_VALUE) {
             response.setContentLength((int) length);
         } else {
@@ -596,44 +597,35 @@ public class DefaultServlet  extends HttpServlet {
             } catch (IllegalStateException e) {
                 // Silent catch
             }
-            if (ostream != null) {
-                FileCopyUtils.copy(resFile, ostream, range);
-            } else {
-                FileCopyUtils.copy(resFile, writer, range, fileEncoding);
-            }
-        }
-    }
-
-
-    private void processFullFile(HttpServletResponse response, boolean content, File resFile, String contentType, long contentLength, ServletOutputStream ostream, PrintWriter writer) throws IOException {
-        // Set the appropriate output headers
-        if (contentType != null) {
-            response.setContentType(contentType);
-        }
-        if ((contentLength >= 0)) {
-            if (contentLength < Integer.MAX_VALUE) {
-                response.setContentLength((int) contentLength);
-            } else {
-                // Set the content-length as String to be able to use a long
-                response.setHeader("content-length", "" + contentLength);
-            }
-        }
-
-        // Copy the input stream to our output stream (if requested)
-        if (content) {
+            InputStream is = null;
             try {
-                response.setBufferSize(output);
-            } catch (IllegalStateException e) {
-                // Silent catch
-            }
-            if (ostream != null) {
-                FileCopyUtils.copy(resFile, ostream);
-            } else {
-                FileCopyUtils.copy(resFile, writer, fileEncoding);
+                is = new FileInputStream(resFile);
+                if (ostream != null) {
+                    if (range == null) {
+                        CopyUtils.copy(is, ostream);
+                    } else {
+                        CopyUtils.copyRange(is, ostream, range.start, range.end);
+                    }
+                } else {Reader reader;
+                    if (fileEncoding == null) {
+                        reader = new InputStreamReader(is);
+                    } else {
+                        reader = new InputStreamReader(is,
+                                                       fileEncoding);
+                    }
+                    if (range == null) {
+                        CopyUtils.copyRange(reader, writer);                        
+                    } else {
+                        CopyUtils.copyRange(reader, writer, range.start, range.end);
+                    }
+                }
+            } finally {
+                is.close();
             }
         }
     }
 
+
     public static String lastModifiedHttp(File resFile) {
         String lastModifiedHttp = null;
         SimpleDateFormat format = (SimpleDateFormat)formatTL.get();
index f67b16f..fa6856c 100644 (file)
@@ -31,14 +31,15 @@ import java.util.ArrayList;
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.tomcat.servlets.util.URLEncoder;
+
 /**
  * Handles directory listing
  */
-public class Dir2Html  extends HttpServlet {
+public class Dir2Html {
 
     /**
      * Array containing the safe characters set.
@@ -113,50 +114,6 @@ public class Dir2Html  extends HttpServlet {
 
     // --------------------------------------------------------- Public Methods
 
-
-    /**
-     * Finalize this servlet.
-     */
-    public void destroy() {
-    }
-
-
-    /**
-     * Initialize this servlet.
-     */
-    public void init() throws ServletException {
-    }
-
-    protected void doGet(HttpServletRequest request,
-                         HttpServletResponse response)
-        throws IOException, ServletException {
-
-        // Serve the requested resource, including the data content
-        //serveResource(request, response, true);
-
-    }
-
-
-    protected void doHead(HttpServletRequest request,
-                          HttpServletResponse response)
-        throws IOException, ServletException {
-
-        // Serve the requested resource, without the data content
-        //serveResource(request, response, false);
-
-    }
-
-
-    protected void doPost(HttpServletRequest request,
-                          HttpServletResponse response)
-        throws IOException, ServletException {
-        doGet(request, response);
-    }
-
-
-
-    
-
     /**
      * Serve the specified resource, optionally including the data content.
      *
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/Filesystem.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/Filesystem.java
new file mode 100644 (file)
index 0000000..8af49f4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ */
+package org.apache.tomcat.servlets.file;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/** 
+ * Abstract the filesystem - lighter than the JNDI used in catalina.
+ * 
+ * This can be used to port the File/Dav servlets to environments that 
+ * don't have a file system access, or in servlet engines with class-based 
+ * sandboxing.
+ */
+public class Filesystem {
+
+    
+    public OutputStream getOutputStream(String name) throws IOException {
+        return null;
+    }
+
+    public InputStream getInputStream(String name) throws IOException {
+        return new FileInputStream(name);
+    }
+    
+    
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/LocalFilesystem.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/LocalFilesystem.java
new file mode 100644 (file)
index 0000000..8c9dbd3
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ */
+package org.apache.tomcat.servlets.file;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+public class LocalFilesystem extends Filesystem {
+
+    public OutputStream getOutputStream(String name) throws IOException {
+        return new FileOutputStream(name);
+    }    
+}
index 31ed402..19a54f1 100644 (file)
@@ -37,10 +37,11 @@ import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.apache.tomcat.servlets.util.FastHttpDateFormat;
 import org.apache.tomcat.servlets.util.Range;
 import org.apache.tomcat.servlets.util.RequestUtil;
 import org.apache.tomcat.servlets.util.UrlUtils;
-import org.apache.tomcat.util.http.FastHttpDateFormat;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
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
new file mode 100644 (file)
index 0000000..9eb2d84
--- /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.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
new file mode 100644 (file)
index 0000000..47578cc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ */
+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;
+    }
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/BaseJspLoader.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/BaseJspLoader.java
new file mode 100644 (file)
index 0000000..87dc0a8
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * 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.jsp;
+
+import java.io.IOException;
+import java.util.Vector;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+
+/** 
+ * Load a JSP generated by jasper.
+ * 
+ * Requires a 'jspc' servlet.
+ * 
+ * This class has no dependencies on Jasper, it uses 2 servlets to integrate.
+ */
+public abstract class BaseJspLoader {
+    boolean usePrecompiled = false;
+    
+    public static interface JspRuntime {
+        public void init(ServletContext ctx);
+    }
+
+    public static interface JspCompiler {
+        public void compileAndInit(ServletContext ctx, String jspUri, 
+                ServletConfig cfg,
+                String classPath, String pkg);
+    }
+    
+    /** 
+     * Load the proxied jsp, if any.
+     * @param config 
+     * @throws ServletException 
+     * @throws IOException 
+     */
+    public Servlet loadProxy(String jspFile, 
+            ServletContext ctx, 
+            ServletConfig config) throws ServletException, IOException {
+        synchronized(this.getClass()) {
+            // So we don't have a direct dep on jasper...
+            Object attribute = ctx.getAttribute("jasper.jspRuntimeContext");
+            if (attribute == null) {
+                try {
+                    Class jsprt = Class.forName("org.apache.tomcat.servlets.jspc.JasperRuntime");
+                    JspRuntime rt = (JspRuntime)jsprt.newInstance();
+                    rt.init(ctx);
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                    return null;
+                }
+            }
+        }
+        String mangledClass = getClassName(ctx, jspFile );
+
+        // TODO: reloading filter: get the class file, 
+        // compare with jsp file, use dependants
+
+
+        HttpServlet jsp = null;
+        Class jspC = null;
+    
+        String cp = getClassPath(ctx);
+        ClassLoader cl = getClassLoader(ctx);
+
+        // Already created
+        if (usePrecompiled) {
+            try {
+                jspC = cl.loadClass(mangledClass);
+            } catch( Throwable t ) {
+                //t.printStackTrace();
+                // Not found - first try 
+            }
+        }
+
+        if (jspC == null) {
+            System.err.println("Recompile " + jspFile);
+            // Class not found - needs to be compiled
+            compileAndInitPage(ctx, jspFile, config, cp);
+        } else {
+            System.err.println("Pre-compiled " + jspFile);            
+        }
+
+        if( jspC == null ) {
+            try {
+                jspC = cl.loadClass(mangledClass);
+            } catch( Throwable t ) {
+                //t.printStackTrace();
+            }
+        }
+        if (jspC == null) {
+            throw new ServletException("Class not found " + mangledClass);
+        }
+
+        try {
+            jsp=(HttpServlet)jspC.newInstance();
+        } catch( Throwable t ) {
+            t.printStackTrace();
+        }
+        jsp.init(config);
+        return jsp;
+    }
+
+    public ClassLoader getClassLoader(ServletContext ctx) {
+        return null;
+    }
+
+    public String getClassPath(ServletContext ctx) {
+        return null;
+    }
+    
+    protected void compileAndInitPage(ServletContext ctx, 
+            String jspUri, 
+            ServletConfig cfg,
+            String classPath) 
+                throws ServletException, IOException {
+        try {
+            Class jsprt = Class.forName("org.apache.tomcat.servlets.jspc.JspcServlet");
+            JspCompiler rt = (JspCompiler) jsprt.newInstance();
+            rt.compileAndInit(ctx, jspUri, cfg, classPath, getPackage(ctx, jspUri));
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }        
+    }    
+
+    public boolean needsReload(String jspFile, Servlet s) {
+        return false;
+    }
+    
+    public String getPackage(ServletContext ctx, String jspUri) {
+        String ver = "v" + ctx.getMajorVersion() + 
+        ctx.getMinorVersion();
+
+        int iSep = jspUri.lastIndexOf('/') + 1;
+        String className = makeJavaIdentifier(jspUri.substring(iSep));
+        String basePackageName = JSP_PACKAGE_NAME;
+
+        iSep--;
+        String derivedPackageName = (iSep > 0) ?
+                makeJavaPackage(jspUri.substring(1,iSep)) : "";
+
+        return ver + "." + basePackageName;
+
+    }
+    
+    /** Convert an identifier to a class name, using jasper conventions
+     * @param ctx 
+     * 
+     * @param jspUri a relative JSP file
+     * @return class name that would be generated by jasper
+     */
+    public String getClassName( ServletContext ctx, String jspUri ) {
+        // Generated code is different for different servlet API versions
+        // We could have a context running in both 2.5 and 3.0 with precompiled
+        // jsps
+        String ver = "v" + ctx.getMajorVersion() + 
+            ctx.getMinorVersion();
+
+        int iSep = jspUri.lastIndexOf('/') + 1;
+        String className = makeJavaIdentifier(jspUri.substring(iSep));
+        String basePackageName = JSP_PACKAGE_NAME;
+
+        iSep--;
+        String derivedPackageName = (iSep > 0) ?
+            makeJavaPackage(jspUri.substring(1,iSep)) : "";
+        if (derivedPackageName.length() == 0) {
+            return basePackageName + "." + className;
+        }
+        
+        return ver + "." + basePackageName + '.' + derivedPackageName + "." + 
+            className;
+    }
+
+    // ------------- Copied from jasper ---------------------------
+
+    private static final String JSP_PACKAGE_NAME = "org.apache.jsp";
+
+    private static final String makeJavaIdentifier(String identifier) {
+        StringBuffer modifiedIdentifier = 
+            new StringBuffer(identifier.length());
+        if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
+            modifiedIdentifier.append('_');
+        }
+        for (int i = 0; i < identifier.length(); i++) {
+            char ch = identifier.charAt(i);
+            if (Character.isJavaIdentifierPart(ch) && ch != '_') {
+                modifiedIdentifier.append(ch);
+            } else if (ch == '.') {
+                modifiedIdentifier.append('_');
+            } else {
+                modifiedIdentifier.append(mangleChar(ch));
+            }
+        }
+        if (isJavaKeyword(modifiedIdentifier.toString())) {
+            modifiedIdentifier.append('_');
+        }
+        return modifiedIdentifier.toString();
+    }
+
+    private static final String javaKeywords[] = {
+        "abstract", "assert", "boolean", "break", "byte", "case",
+        "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends",
+        "final", "finally", "float", "for", "goto",
+        "if", "implements", "import", "instanceof", "int",
+        "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short",
+        "static", "strictfp", "super", "switch", "synchronized",
+        "this", "throws", "transient", "try", "void",
+        "volatile", "while" };
+
+    private static final String makeJavaPackage(String path) {
+        String classNameComponents[] = split(path,"/");
+        StringBuffer legalClassNames = new StringBuffer();
+        for (int i = 0; i < classNameComponents.length; i++) {
+            legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
+            if (i < classNameComponents.length - 1) {
+                legalClassNames.append('.');
+            }
+        }
+        return legalClassNames.toString();
+    }
+
+    private static final String [] split(String path, String pat) {
+        Vector comps = new Vector();
+        int pos = path.indexOf(pat);
+        int start = 0;
+        while( pos >= 0 ) {
+            if(pos > start ) {
+                String comp = path.substring(start,pos);
+                comps.add(comp);
+            }
+            start = pos + pat.length();
+            pos = path.indexOf(pat,start);
+        }
+        if( start < path.length()) {
+            comps.add(path.substring(start));
+        }
+        String [] result = new String[comps.size()];
+        for(int i=0; i < comps.size(); i++) {
+            result[i] = (String)comps.elementAt(i);
+        }
+        return result;
+    }
+            
+
+    /**
+     * Test whether the argument is a Java keyword
+     */
+    private static boolean isJavaKeyword(String key) {
+        int i = 0;
+        int j = javaKeywords.length;
+        while (i < j) {
+            int k = (i+j)/2;
+            int result = javaKeywords[k].compareTo(key);
+            if (result == 0) {
+                return true;
+            }
+            if (result < 0) {
+                i = k+1;
+            } else {
+                j = k;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Mangle the specified character to create a legal Java class name.
+     */
+    private static final String mangleChar(char ch) {
+        char[] result = new char[5];
+        result[0] = '_';
+        result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
+        result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
+        result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
+        result[4] = Character.forDigit(ch & 0xf, 16);
+        return new String(result);
+    }
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java
deleted file mode 100644 (file)
index 4c74405..0000000
+++ /dev/null
@@ -1,173 +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.jsp;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-
-import javax.naming.NamingException;
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
-import org.apache.jasper.EmbeddedServletOptions;
-import org.apache.jasper.JasperException;
-import org.apache.jasper.JspC;
-import org.apache.jasper.Options;
-import org.apache.jasper.compiler.JspRuntimeContext;
-import org.apache.jasper.servlet.JspServletWrapper;
-import org.apache.tomcat.InstanceManager;
-
-/** 
- * The actual compiler. Maps and compile a jsp-file to a class.
- */
-public class JasperCompilerTemplateClassMapper 
-        extends SimpleTemplateClassMapper {
-    
-    public void init(ServletConfig config) {
-        this.config = config;
-        ServletContext context = config.getServletContext();
-        context.setAttribute(InstanceManager.class.getName(), 
-                new InstanceManager() {
-
-                    public void destroyInstance(Object arg0)
-                            throws IllegalAccessException,
-                            InvocationTargetException {
-                    }
-
-                    public Object newInstance(String arg0)
-                            throws IllegalAccessException,
-                            InvocationTargetException, NamingException,
-                            InstantiationException, ClassNotFoundException {
-                        return newInstance(arg0, 
-                                this.getClass().getClassLoader());
-                    }
-
-                    public void newInstance(Object o)
-                            throws IllegalAccessException,
-                            InvocationTargetException, NamingException {
-                    }
-
-                    public Object newInstance(String className,
-                                              ClassLoader classLoader)
-                            throws IllegalAccessException,
-                            InvocationTargetException, NamingException,
-                            InstantiationException, ClassNotFoundException {
-                        Class clazz = classLoader.loadClass(className);
-                        return clazz.newInstance();
-                    }
-            
-        });
-        //      Initialize the JSP Runtime Context
-        options = new EmbeddedServletOptions(config, context);
-        
-        rctxt = new JspRuntimeContext(context, options);
-        String basePath = context.getRealPath("/");
-        File f = new File(basePath + "/WEB-INF/classes");
-        f.mkdirs();
-        //fileS.initParams.put("scratchdir",  f.getAbsolutePath());
-        // if load-on-startup: allow other servlets to find us
-
-        
-    }
-    
-    private Options options;
-    private JspRuntimeContext rctxt;
-    private ServletConfig config;
-
-    public boolean needsReload(String jspFile, Servlet s) {
-        JspServletWrapper wrapper =
-            (JspServletWrapper) rctxt.getWrapper(jspFile);
-        // TODO: extract outdate info, compilation date, etc
-        return false;
-    }
-    
-    protected Servlet compileAndInitPage(ServletContext ctx, 
-                                         String jspUri, 
-                                         ServletConfig cfg) 
-    throws ServletException {
-        try {
-            if (config == null) {
-                init(cfg);
-            }
-            JspServletWrapper wrapper =
-                (JspServletWrapper) rctxt.getWrapper(jspUri);
-            if (wrapper == null) {
-                synchronized(this) {
-                    wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
-                    if (wrapper == null) {
-                        // Check if the requested JSP page exists, to avoid
-                        // creating unnecessary directories and files.
-                        if (null == ctx.getResource(jspUri)) {
-                            return null;
-                        }
-                        //boolean isErrorPage = exception != null;
-                        wrapper = new JspServletWrapper(cfg, options, jspUri,
-                                false, rctxt);
-                        rctxt.addWrapper(jspUri,wrapper);
-                    }
-                }
-            }
-
-            wrapper.getJspEngineContext().compile();
-            return wrapper.getServlet();
-        } catch (IOException ex) {
-            throw new ServletException(ex);
-        }
-    }
-
-    /**
-     *  
-     * Do the compilation - without JspServletWrapper
-     * 
-     * Options: 
-     *  - jasper.jar in classpath, we do Class.forName for main()
-     *  - TODO: exec jasper.sh ( or any other script set in params ) 
-     *  - TODO: redirect to a different servlet
-     * 
-     * Not used right - see previous method for a safer approach
-     * 
-     * @param ctx
-     * @param jspPath
-     */
-   public void compileJspDirect(ServletContext ctx, String jspPath) {
-        //ServletContextImpl ctx = (ServletContextImpl)sctx;
-        // Params to pass to jspc:
-        // classpath 
-        // webapp base dir
-        String baseDir = ctx.getRealPath("/");
-        // jsp path ( rel. base dir )
-
-        JspC jspc = new JspC();
-        jspc.setUriroot(baseDir);
-        jspc.setTrimSpaces(false);
-        jspc.setPoolingEnabled(true);
-        jspc.setErrorOnUseBeanInvalidClassAttribute(false);
-        jspc.setClassDebugInfo(true);
-        jspc.setCaching(true);
-        jspc.setSmapDumped(true);
-
-        try {
-            jspc.execute();
-        } catch (JasperException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java
deleted file mode 100644 (file)
index 85c1cc0..0000000
+++ /dev/null
@@ -1,90 +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.jsp;
-
-import java.io.IOException;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.tomcat.addons.UserTemplateClassMapper;
-import org.apache.tomcat.integration.ObjectManager;
-
-/**
- * Support for <servlet><jsp-file> config. 
- * 
- * A servlet can be configured with a jsp-file instead of a class. This can
- * be translated into a regular servlet, with JspFileTemplateServlet class and
- * the jsp-file as init parameter.  
- * 
- * This servlet is not jsp specific - you can put any templating file in the 
- * jsp-file config ( if you can ignore the jsp in the name of the attribute ).
- * The file will be converted to a servlet by a custom addon ( jasper in most
- * cases ) 
- * 
- * @author Costin Manolache
- */
-public class JspFileTemplateServlet extends HttpServlet {
-    
-    String jspFile; 
-    Servlet realJspServlet;
-
-    UserTemplateClassMapper mapper;
-
-    /** 
-     * If called from a <jsp-file> servlet, compile the servlet and init it.
-     */
-    public void init(ServletConfig config) throws ServletException {
-        super.init(config);
-        // Support <servlet><jsp-file>
-        String jspFileParam = config.getInitParameter("jsp-file");
-        // TODO: use extension to find the right UserTemplateClassMapper.
-        ObjectManager om = 
-            (ObjectManager) config.getServletContext().getAttribute(ObjectManager.ATTRIBUTE);
-        mapper = 
-            (UserTemplateClassMapper) om.get(
-                    UserTemplateClassMapper.class);
-        if (mapper == null) {
-            mapper = new SimpleTemplateClassMapper();
-        }
-        
-        if (jspFile == null && jspFileParam != null) {
-            jspFile = jspFileParam;
-        }
-        // exception will be thrown if not set properly
-        realJspServlet = mapper.loadProxy(jspFile, getServletContext(), 
-                config);
-        realJspServlet.init(config);
-    }
-
-    public void setJspFile(String jspFile) {
-        this.jspFile = jspFile;
-    }
-    
-    protected void service(HttpServletRequest req, HttpServletResponse res)
-        throws ServletException, IOException 
-    {
-        // TODO: support reload
-        
-        // realJspServlet will be set - init will fail otherwise.
-        realJspServlet.service(req, res);
-    }
-} 
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/SimpleTemplateClassMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/SimpleTemplateClassMapper.java
deleted file mode 100644 (file)
index d37db88..0000000
+++ /dev/null
@@ -1,214 +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.jsp;
-
-import java.util.Vector;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-
-import org.apache.tomcat.addons.UserTemplateClassMapper;
-
-/** 
- * Extracted from Jasper - maps a template file ( foo/my.jsp ) to a classname
- * that is generated by the UserTemplateCompiler.
- * 
- * TODO: transform this to an interface, paired with UserTemplateCompiler.
- * 
- * @author Costin Manolache
- */
-public class SimpleTemplateClassMapper implements UserTemplateClassMapper {
-
-    /** 
-     * Load the proxied jsp, if any.
-     * @param config 
-     * @throws ServletException 
-     */
-    public Servlet loadProxy(String jspFile, 
-                             ServletContext ctx, 
-                             ServletConfig config) throws ServletException {
-        String mangledClass = getClassName( jspFile );
-
-        HttpServlet jsp = null;
-        Class jspC = null;
-        
-        // Already created
-        if( jspC == null ) {
-            try {
-                jspC=Class.forName( mangledClass );
-            } catch( Throwable t ) {
-                // Not found - first try 
-            }
-        }
-        
-        if (jspC == null) {
-            // Class not found - needs to be compiled
-            return compileAndInitPage(ctx, jspFile, config);
-        }
-        
-        try {
-            jsp=(HttpServlet)jspC.newInstance();
-        } catch( Throwable t ) {
-            t.printStackTrace();
-        }
-        jsp.init(config);
-        return jsp;
-    }
-    
-    public boolean needsReload(String jspFile, Servlet s) {
-        return false;
-    }
-    
-    protected Servlet compileAndInitPage(ServletContext ctx, 
-                                      String jspUri, 
-                                      ServletConfig cfg) 
-        throws ServletException {
-        throw new ServletException("Pre-compiled page not found, please " +
-                       "add a compiler addon to compile at runtime");
-    }
-    
-    /** Convert an identifier to a class name, using jasper conventions
-     * 
-     * @param jspUri a relative JSP file
-     * @return class name that would be generated by jasper
-     */
-    public String getClassName( String jspUri ) {
-        int iSep = jspUri.lastIndexOf('/') + 1;
-        String className = makeJavaIdentifier(jspUri.substring(iSep));
-        String basePackageName = JSP_PACKAGE_NAME;
-
-        iSep--;
-        String derivedPackageName = (iSep > 0) ?
-                makeJavaPackage(jspUri.substring(1,iSep)) : "";
-        
-        if (derivedPackageName.length() == 0) {
-            return basePackageName + "." + className;
-        }
-        return basePackageName + '.' + derivedPackageName + "." + className;
-    }
-
-    // ------------- Copied from jasper ---------------------------
-
-    private static final String JSP_PACKAGE_NAME = "org.apache.jsp";
-
-    private static final String makeJavaIdentifier(String identifier) {
-        StringBuilder modifiedIdentifier = 
-            new StringBuilder(identifier.length());
-        if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
-            modifiedIdentifier.append('_');
-        }
-        for (int i = 0; i < identifier.length(); i++) {
-            char ch = identifier.charAt(i);
-            if (Character.isJavaIdentifierPart(ch) && ch != '_') {
-                modifiedIdentifier.append(ch);
-            } else if (ch == '.') {
-                modifiedIdentifier.append('_');
-            } else {
-                modifiedIdentifier.append(mangleChar(ch));
-            }
-        }
-        if (isJavaKeyword(modifiedIdentifier.toString())) {
-            modifiedIdentifier.append('_');
-        }
-        return modifiedIdentifier.toString();
-    }
-
-    private static final String javaKeywords[] = {
-        "abstract", "assert", "boolean", "break", "byte", "case",
-        "catch", "char", "class", "const", "continue",
-        "default", "do", "double", "else", "enum", "extends",
-        "final", "finally", "float", "for", "goto",
-        "if", "implements", "import", "instanceof", "int",
-        "interface", "long", "native", "new", "package",
-        "private", "protected", "public", "return", "short",
-        "static", "strictfp", "super", "switch", "synchronized",
-        "this", "throws", "transient", "try", "void",
-        "volatile", "while" };
-
-    private static final String makeJavaPackage(String path) {
-        String classNameComponents[] = split(path,"/");
-        StringBuilder legalClassNames = new StringBuilder();
-        for (int i = 0; i < classNameComponents.length; i++) {
-            legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
-            if (i < classNameComponents.length - 1) {
-                legalClassNames.append('.');
-            }
-        }
-        return legalClassNames.toString();
-    }
-
-    private static final String [] split(String path, String pat) {
-        Vector comps = new Vector();
-        int pos = path.indexOf(pat);
-        int start = 0;
-        while( pos >= 0 ) {
-            if(pos > start ) {
-                String comp = path.substring(start,pos);
-                comps.add(comp);
-            }
-            start = pos + pat.length();
-            pos = path.indexOf(pat,start);
-        }
-        if( start < path.length()) {
-            comps.add(path.substring(start));
-        }
-        String [] result = new String[comps.size()];
-        for(int i=0; i < comps.size(); i++) {
-            result[i] = (String)comps.elementAt(i);
-        }
-        return result;
-    }
-            
-
-    /**
-     * Test whether the argument is a Java keyword
-     */
-    private static boolean isJavaKeyword(String key) {
-        int i = 0;
-        int j = javaKeywords.length;
-        while (i < j) {
-            int k = (i+j)/2;
-            int result = javaKeywords[k].compareTo(key);
-            if (result == 0) {
-                return true;
-            }
-            if (result < 0) {
-                i = k+1;
-            } else {
-                j = k;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Mangle the specified character to create a legal Java class name.
-     */
-    private static final String mangleChar(char ch) {
-        char[] result = new char[5];
-        result[0] = '_';
-        result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
-        result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
-        result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
-        result[4] = Character.forDigit(ch & 0xf, 16);
-        return new String(result);
-    }
-    
-}
index 609dbc2..41a98e5 100644 (file)
@@ -22,13 +22,11 @@ import java.util.HashMap;
 import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.tomcat.addons.UserTemplateClassMapper;
-import org.apache.tomcat.integration.ObjectManager;
-
 
 /** 
  *
@@ -39,20 +37,22 @@ public class WildcardTemplateServlet extends HttpServlet {
     // for the '*.jsp' case - need to keep track of the jsps
     HashMap<String, Servlet> jsps=new HashMap<String, Servlet>();
 
-    UserTemplateClassMapper mapper;
+    BaseJspLoader mapper;
     
     /** 
      * If called from a <jsp-file> servlet, compile the servlet and init it.
      */
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
-        ObjectManager om = 
-            (ObjectManager) config.getServletContext().getAttribute(ObjectManager.ATTRIBUTE);
-        mapper = 
-            (UserTemplateClassMapper) om.get(
-                    UserTemplateClassMapper.class);
-        if (mapper == null) {
-            mapper = new SimpleTemplateClassMapper();
+        String mapperCN = config.getInitParameter("mapper");
+        if (mapperCN == null) {
+            throw new UnavailableException("can't create mapper");
+        }
+        try {
+            Class c = Class.forName(mapperCN);
+            mapper = (BaseJspLoader) c.newInstance();
+        } catch (Throwable t) {
+            throw new UnavailableException("can't create mapper");
         }
     }
 
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JasperRuntime.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JasperRuntime.java
new file mode 100644 (file)
index 0000000..df8e802
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ */
+package org.apache.tomcat.servlets.jspc;
+
+import java.io.File;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.jasper.Options;
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.tomcat.servlets.jsp.BaseJspLoader;
+
+public class JasperRuntime extends HttpServlet implements BaseJspLoader.JspRuntime {
+
+    // TODO: add DefaultAnnotationProcessor
+    // TODO: implement the options
+    private JspRuntimeContext jspRuntimeContext;
+
+    public void init(ServletConfig cfg) throws ServletException {
+        super.init(cfg);
+        ServletContext ctx = cfg.getServletContext();
+        init(ctx);
+    }
+    
+    public void doGet(HttpServletRequest req, HttpServletResponse res) {
+        
+    }
+
+    @Override
+    public void init(ServletContext ctx) {
+        jspRuntimeContext = new JspRuntimeContext(ctx, new Options() {
+            @Override
+            public boolean genStringAsCharArray() {
+                return false;
+            }
+
+            @Override
+            public Map getCache() {
+                return null;
+            }
+
+            @Override
+            public int getCheckInterval() {
+                return 0;
+            }
+
+            @Override
+            public boolean getClassDebugInfo() {
+                return false;
+            }
+
+            @Override
+            public String getClassPath() {
+                return null;
+            }
+
+            @Override
+            public String getCompiler() {
+                return null;
+            }
+
+            @Override
+            public String getCompilerClassName() {
+                return null;
+            }
+
+            @Override
+            public String getCompilerSourceVM() {
+                return null;
+            }
+
+            @Override
+            public String getCompilerTargetVM() {
+                return null;
+            }
+
+            @Override
+            public boolean getDevelopment() {
+                return false;
+            }
+
+            @Override
+            public boolean getDisplaySourceFragment() {
+                return false;
+            }
+
+            @Override
+            public boolean getErrorOnUseBeanInvalidClassAttribute() {
+                return false;
+            }
+
+            @Override
+            public boolean getFork() {
+                return false;
+            }
+
+            @Override
+            public String getIeClassId() {
+                return null;
+            }
+
+            @Override
+            public String getJavaEncoding() {
+                return null;
+            }
+
+            @Override
+            public JspConfig getJspConfig() {
+                return null;
+            }
+
+            @Override
+            public boolean getKeepGenerated() {
+                return false;
+            }
+
+            @Override
+            public boolean getMappedFile() {
+                return false;
+            }
+
+            @Override
+            public int getModificationTestInterval() {
+                return 0;
+            }
+
+            @Override
+            public File getScratchDir() {
+                return null;
+            }
+
+            public boolean getSendErrorToClient() {
+                return false;
+            }
+
+            @Override
+            public TagPluginManager getTagPluginManager() {
+                return null;
+            }
+
+            @Override
+            public TldLocationsCache getTldLocationsCache() {
+                return null;
+            }
+
+            @Override
+            public boolean getTrimSpaces() {
+                return false;
+            }
+
+            @Override
+            public boolean isCaching() {
+                return false;
+            }
+
+            @Override
+            public boolean isPoolingEnabled() {
+                return false;
+            }
+
+            @Override
+            public boolean isSmapDumped() {
+                return false;
+            }
+
+            @Override
+            public boolean isSmapSuppressed() {
+                return false;
+            }
+
+            @Override
+            public boolean isXpoweredBy() {
+                return false;
+            }
+        });
+        
+        ctx.setAttribute("jasper.jspRuntimeContext", jspRuntimeContext);
+    }
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JspcServlet.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/jspc/JspcServlet.java
new file mode 100644 (file)
index 0000000..f2b7105
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.jspc;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspC;
+import org.apache.tomcat.servlets.jsp.BaseJspLoader;
+
+/** 
+ * The actual compiler. Maps and compile a jsp-file to a class.
+ */
+public class JspcServlet extends HttpServlet implements BaseJspLoader.JspCompiler {
+    
+    public void doGet(HttpServletRequest req, HttpServletResponse res) 
+                throws ServletException {
+
+        // TODO: allow only local calls ?
+        
+        // relative to context
+        String jspFiles = req.getParameter("jspFiles");
+        String classPath = req.getParameter("classPath");
+        String pkg = req.getParameter("pkg");
+        
+        compileAndInit(getServletContext(), 
+                jspFiles, getServletConfig(), classPath, pkg);
+    }
+
+    @Override
+    public void compileAndInit(ServletContext ctx, String jspFiles,
+            ServletConfig cfg, String classPath, String pkg) {
+        
+        if (jspFiles.startsWith("/")) {
+            jspFiles = jspFiles.substring(1);
+        }
+        String baseDir = ctx.getRealPath("/");
+        
+        JspC jspc = new JspC();
+        
+        jspc.setUriroot(baseDir);
+        jspc.setTrimSpaces(false);
+        jspc.setPoolingEnabled(true);
+        jspc.setErrorOnUseBeanInvalidClassAttribute(false);
+        jspc.setClassDebugInfo(true);
+        jspc.setCaching(true);
+        jspc.setSmapDumped(true);
+        jspc.setGenStringAsCharArray(true);
+        
+        jspc.setJspFiles(jspFiles);
+        
+        jspc.setVerbose(10);
+        
+        jspc.setPackage(pkg);
+
+        jspc.setOutputDir(baseDir + "WEB-INF/tmp");
+        jspc.setCompile(true);
+        //jspc.setCompiler("jdt");
+        jspc.setClassPath(classPath);
+
+        try {
+            jspc.execute();
+        } catch (JasperException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
index 5af1e7f..2120c44 100644 (file)
@@ -32,7 +32,6 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.tomcat.addons.UserAuthentication;
 
 
 /**
index c17c829..cfcb212 100644 (file)
@@ -23,7 +23,6 @@ import java.security.Principal;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.tomcat.addons.UserAuthentication;
 
 
 /**
index 5535790..f17c6a0 100644 (file)
@@ -23,7 +23,6 @@ import java.security.Principal;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.tomcat.addons.UserAuthentication;
 
 
 /**
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/UserAuthentication.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/sec/UserAuthentication.java
new file mode 100644 (file)
index 0000000..62b4b97
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ */
+package org.apache.tomcat.servlets.sec;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Plugin for user auth.
+ * 
+ * This interface should support all common forms of auth, 
+ * including Basic, Digest, Form and various other auth 
+ * standards - the plugin has full control over request and
+ * response.  
+ * 
+ * Container will verify the security constraints on URLs and 
+ * call this for all URLs that have constraints. The plugin can
+ * either authenticate and return the principal, or change 
+ * the response - redirect, add headers, send content. 
+ * 
+ * Alternative: a simple Filter can do the same, with some conventions
+ * to support it ( attributes ).
+ * 
+ * @author Costin Manolache
+ */
+public interface UserAuthentication {
+
+    /**
+     * If req has all the info - return the principal.
+     * Otherwise set the challenge in response.
+     * 
+     * @param requestedMethod auth method from web.xml. Spec
+     *  complain plugins must support it. 
+     * @throws IOException 
+     */
+    public Principal authenticate(HttpServletRequest req, 
+                                  HttpServletResponse res, 
+                                  String requestedMethod) throws IOException;
+    
+    
+    public boolean isUserInRole(HttpServletRequest req,
+                                Principal p,
+                                String role);
+}
index 22e86de..6bd1a33 100644 (file)
@@ -363,4 +363,4 @@ public class RandomGenerator {
     public void setSessionIdLength(int idLength) {
         this.sessionIdLength = idLength;
     }
-}
\ No newline at end of file
+}
index ceedb5d..64555a6 100644 (file)
@@ -33,7 +33,6 @@ import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
-import org.apache.tomcat.addons.UserSessionManager;
 
 // TODO: move 'expiring objects' to a separate utility class
 // TODO: hook the background thread
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/UserSessionManager.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/UserSessionManager.java
new file mode 100644 (file)
index 0000000..82c30c0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ */
+package org.apache.tomcat.servlets.session;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Session management plugin. No dependency on tomcat-lite, should
+ * be possible to add this to tomcat-trunk or other containers.
+ * 
+ * The container will:
+ * - extract the session id from request ( via a filter or built-ins )
+ * - call this interface when the user makes the related calls in the 
+ * servlet API.
+ * - provide a context attribute 'context-listeners' with the 
+ * List<EventListener> from web.xml 
+ *
+ * Implementation of this class must provide HttpSession object 
+ * and implement the spec. 
+ * 
+ */
+public interface UserSessionManager {
+
+    
+    
+    HttpSession findSession(String requestedSessionId) throws IOException;
+
+    HttpSession createSession(String requestedSessionId);
+  
+    boolean isValid(HttpSession session);
+
+    void access(HttpSession session);
+  
+    void endAccess(HttpSession session);
+  
+  
+    void setSessionTimeout(int to);
+    
+    void setContext(ServletContext ctx);
+
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java
new file mode 100644 (file)
index 0000000..1f42286
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ *  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.util;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Revision: 467222 $ $Date: 2006-10-23 20:17:11 -0700 (Mon, 23 Oct 2006) $
+ */
+
+public final class Base64 {
+
+
+    private static Logger log=
+        Logger.getLogger( Base64.class.getName() );
+    
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 63;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+
+
+    static private final byte PAD               = ( byte ) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH + 1];
+
+    static {
+
+        for (int i = 0; i<BASELENGTH; i++ ) {
+            base64Alphabet[i] = -1;
+        }
+        for ( int i = 'Z'; i >= 'A'; i-- ) {
+            base64Alphabet[i] = (byte) (i-'A');
+        }
+        for ( int i = 'z'; i>= 'a'; i--) {
+            base64Alphabet[i] = (byte) ( i-'a' + 26);
+        }
+
+        for ( int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i-'0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+        String lookupString = 
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
+            "0123456789+/";
+        
+       for (int i = 0; i < lookupString.length(); i++ ) {
+            lookUpBase64Alphabet[i] = (byte) lookupString.charAt(i);
+       }
+    }
+
+
+    static boolean isBase64( byte octect ) {
+        //shall we ignore white space? JEFF??
+        return(octect == PAD || base64Alphabet[octect] != -1 );
+    }
+
+
+    static boolean isArrayByteBase64( byte[] arrayOctect ) {
+        int length = arrayOctect.length;
+        if ( length == 0 )
+            return false;
+        for ( int i=0; i < length; i++ ) {
+            if ( Base64.isBase64( arrayOctect[i] ) == false)
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static byte[] encode( byte[] binaryData ) {
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        byte     encodedData[]     = null;
+
+
+        if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
+            encodedData = new byte[ (numberTriplets + 1 )*4  ];
+        else // 16 or 8 bit
+            encodedData = new byte[ numberTriplets*4 ];
+
+        int k=0, l=0, b1=0,b2=0,b3=0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        int i           = 0;
+        for ( i = 0; i<numberTriplets; i++ ) {
+
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex] & 0xFF;
+            b2 = binaryData[dataIndex + 1] & 0xFF;
+            b3 = binaryData[dataIndex + 2] & 0xFF;
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i*4;
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
+( k<<4 )];
+            encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
+( b3>>6)];
+            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex    = i*3;
+        encodedIndex = i*4;
+        if (fewerThan24bits == EIGHTBIT ) {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if ( fewerThan24bits == SIXTEENBIT ) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = ( byte ) ( b2 &0x0f );
+            k = ( byte ) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
+| ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+        return encodedData;
+    }
+
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode( byte[] base64Data ) {
+        int      numberQuadruple    = base64Data.length/FOURBYTE;
+        byte     decodedData[]      = null;
+        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+        // Throw away anything not in base64Data
+        // Adjust size
+
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        decodedData      = new byte[ numberQuadruple*3 + 1 ];
+
+        for (int i = 0; i<numberQuadruple; i++ ) {
+            dataIndex = i*4;
+            marker0   = base64Data[dataIndex +2];
+            marker1   = base64Data[dataIndex +3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+            if ( marker0 != PAD && marker1 != PAD ) {     //No PAD e.g 3cQl
+                b3 = base64Alphabet[ marker0 ];
+                b4 = base64Alphabet[ marker1 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
+            } else if ( marker0 == PAD ) {               //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
+                decodedData[encodedIndex+2] = (byte) 0;
+            } else if ( marker1 == PAD ) {              //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ marker0 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6);
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+
+    }
+
+    static final int base64[]= {
+       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+           52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+           64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+           15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+           64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+           41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+    };
+
+    public static String base64Decode( String orig ) {
+       char chars[]=orig.toCharArray();
+       StringBuffer sb=new StringBuffer();
+       int i=0;
+
+       int shift = 0;   // # of excess bits stored in accum
+       int acc = 0;
+       
+       for (i=0; i<chars.length; i++) {
+           int v = base64[ chars[i] & 0xFF ];
+           
+           if ( v >= 64 ) {
+               if( chars[i] != '=' )
+                   if (log.isLoggable(Level.INFO))
+                       log.info("Wrong char in base64: " + chars[i]);
+           } else {
+               acc= ( acc << 6 ) | v;
+               shift += 6;
+               if ( shift >= 8 ) {
+                   shift -= 8;
+                   sb.append( (char) ((acc >> shift) & 0xff));
+               }
+           }
+       }
+       return sb.toString();
+    }
+
+
+}
+
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java
new file mode 100644 (file)
index 0000000..7c2f905
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  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.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class to generate HTTP dates.
+ * 
+ * @author Remy Maucherat
+ */
+public final class FastHttpDateFormat {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    protected static final int CACHE_SIZE = 
+        Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
+
+    
+    /**
+     * HTTP date format.
+     */
+    protected static final SimpleDateFormat format = 
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+
+        format.setTimeZone(gmtZone);
+
+        formats[0].setTimeZone(gmtZone);
+        formats[1].setTimeZone(gmtZone);
+        formats[2].setTimeZone(gmtZone);
+
+    }
+
+
+    /**
+     * Instant on which the currentDate object was generated.
+     */
+    protected static long currentDateGenerated = 0L;
+
+
+    /**
+     * Current formatted date.
+     */
+    protected static String currentDate = null;
+
+
+    /**
+     * Formatter cache.
+     */
+    protected static final ConcurrentHashMap<Long, String> formatCache = 
+        new ConcurrentHashMap<Long, String>(CACHE_SIZE);
+
+
+    /**
+     * Parser cache.
+     */
+    protected static final ConcurrentHashMap<String, Long> parseCache = 
+        new ConcurrentHashMap<String, Long>(CACHE_SIZE);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get the current date in HTTP format.
+     */
+    public static final String getCurrentDate() {
+
+        long now = System.currentTimeMillis();
+        if ((now - currentDateGenerated) > 1000) {
+            synchronized (format) {
+                if ((now - currentDateGenerated) > 1000) {
+                    currentDateGenerated = now;
+                    currentDate = format.format(new Date(now));
+                }
+            }
+        }
+        return currentDate;
+
+    }
+
+
+    /**
+     * Get the HTTP format of the specified date.
+     */
+    public static final String formatDate
+        (long value, DateFormat threadLocalformat) {
+
+        Long longValue = new Long(value);
+        String cachedDate = formatCache.get(longValue);
+        if (cachedDate != null)
+            return cachedDate;
+
+        String newDate = null;
+        Date dateValue = new Date(value);
+        if (threadLocalformat != null) {
+            newDate = threadLocalformat.format(dateValue);
+            updateFormatCache(longValue, newDate);
+        } else {
+            synchronized (formatCache) {
+                synchronized (format) {
+                    newDate = format.format(dateValue);
+                }
+                updateFormatCache(longValue, newDate);
+            }
+        }
+        return newDate;
+
+    }
+
+
+    /**
+     * Try to parse the given date as a HTTP date.
+     */
+    public static final long parseDate(String value, 
+                                       DateFormat[] threadLocalformats) {
+
+        Long cachedDate = parseCache.get(value);
+        if (cachedDate != null)
+            return cachedDate.longValue();
+
+        Long date = null;
+        if (threadLocalformats != null) {
+            date = internalParseDate(value, threadLocalformats);
+            updateParseCache(value, date);
+        } else {
+            synchronized (parseCache) {
+                date = internalParseDate(value, formats);
+                updateParseCache(value, date);
+            }
+        }
+        if (date == null) {
+            return (-1L);
+        } else {
+            return date.longValue();
+        }
+
+    }
+
+
+    /**
+     * Parse date with given formatters.
+     */
+    private static final Long internalParseDate
+        (String value, DateFormat[] formats) {
+        Date date = null;
+        for (int i = 0; (date == null) && (i < formats.length); i++) {
+            try {
+                date = formats[i].parse(value);
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        if (date == null) {
+            return null;
+        }
+        return new Long(date.getTime());
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateFormatCache(Long key, String value) {
+        if (value == null) {
+            return;
+        }
+        if (formatCache.size() > CACHE_SIZE) {
+            formatCache.clear();
+        }
+        formatCache.put(key, value);
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateParseCache(String key, Long value) {
+        if (value == null) {
+            return;
+        }
+        if (parseCache.size() > CACHE_SIZE) {
+            parseCache.clear();
+        }
+        parseCache.put(key, value);
+    }
+
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java
new file mode 100644 (file)
index 0000000..a173195
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ *  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.util;
+
+import java.net.FileNameMap;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * A mime type map that implements the java.net.FileNameMap interface.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ */
+public class MimeMap implements FileNameMap {
+
+    // Defaults - all of them are "well-known" types,
+    // you can add using normal web.xml.
+    
+    public static Hashtable<String,String> defaultMap =
+        new Hashtable<String,String>(101);
+    static {
+        defaultMap.put("txt", "text/plain");
+        defaultMap.put("css", "text/css");
+        defaultMap.put("html","text/html");
+        defaultMap.put("htm", "text/html");
+        defaultMap.put("gif", "image/gif");
+        defaultMap.put("jpg", "image/jpeg");
+        defaultMap.put("jpe", "image/jpeg");
+        defaultMap.put("jpeg", "image/jpeg");
+        defaultMap.put("png", "image/png");
+                defaultMap.put("java", "text/plain");
+        defaultMap.put("body", "text/html");
+        defaultMap.put("rtx", "text/richtext");
+        defaultMap.put("tsv", "text/tab-separated-values");
+        defaultMap.put("etx", "text/x-setext");
+        defaultMap.put("ps", "application/x-postscript");
+        defaultMap.put("class", "application/java");
+        defaultMap.put("csh", "application/x-csh");
+        defaultMap.put("sh", "application/x-sh");
+        defaultMap.put("tcl", "application/x-tcl");
+        defaultMap.put("tex", "application/x-tex");
+        defaultMap.put("texinfo", "application/x-texinfo");
+        defaultMap.put("texi", "application/x-texinfo");
+        defaultMap.put("t", "application/x-troff");
+        defaultMap.put("tr", "application/x-troff");
+        defaultMap.put("roff", "application/x-troff");
+        defaultMap.put("man", "application/x-troff-man");
+        defaultMap.put("me", "application/x-troff-me");
+        defaultMap.put("ms", "application/x-wais-source");
+        defaultMap.put("src", "application/x-wais-source");
+        defaultMap.put("zip", "application/zip");
+        defaultMap.put("bcpio", "application/x-bcpio");
+        defaultMap.put("cpio", "application/x-cpio");
+        defaultMap.put("gtar", "application/x-gtar");
+        defaultMap.put("shar", "application/x-shar");
+        defaultMap.put("sv4cpio", "application/x-sv4cpio");
+        defaultMap.put("sv4crc", "application/x-sv4crc");
+        defaultMap.put("tar", "application/x-tar");
+        defaultMap.put("ustar", "application/x-ustar");
+        defaultMap.put("dvi", "application/x-dvi");
+        defaultMap.put("hdf", "application/x-hdf");
+        defaultMap.put("latex", "application/x-latex");
+        defaultMap.put("bin", "application/octet-stream");
+        defaultMap.put("oda", "application/oda");
+        defaultMap.put("pdf", "application/pdf");
+        defaultMap.put("ps", "application/postscript");
+        defaultMap.put("eps", "application/postscript");
+        defaultMap.put("ai", "application/postscript");
+        defaultMap.put("rtf", "application/rtf");
+        defaultMap.put("nc", "application/x-netcdf");
+        defaultMap.put("cdf", "application/x-netcdf");
+        defaultMap.put("cer", "application/x-x509-ca-cert");
+        defaultMap.put("exe", "application/octet-stream");
+        defaultMap.put("gz", "application/x-gzip");
+        defaultMap.put("Z", "application/x-compress");
+        defaultMap.put("z", "application/x-compress");
+        defaultMap.put("hqx", "application/mac-binhex40");
+        defaultMap.put("mif", "application/x-mif");
+        defaultMap.put("ief", "image/ief");
+        defaultMap.put("tiff", "image/tiff");
+        defaultMap.put("tif", "image/tiff");
+        defaultMap.put("ras", "image/x-cmu-raster");
+        defaultMap.put("pnm", "image/x-portable-anymap");
+        defaultMap.put("pbm", "image/x-portable-bitmap");
+        defaultMap.put("pgm", "image/x-portable-graymap");
+        defaultMap.put("ppm", "image/x-portable-pixmap");
+        defaultMap.put("rgb", "image/x-rgb");
+        defaultMap.put("xbm", "image/x-xbitmap");
+        defaultMap.put("xpm", "image/x-xpixmap");
+        defaultMap.put("xwd", "image/x-xwindowdump");
+        defaultMap.put("au", "audio/basic");
+        defaultMap.put("snd", "audio/basic");
+        defaultMap.put("aif", "audio/x-aiff");
+        defaultMap.put("aiff", "audio/x-aiff");
+        defaultMap.put("aifc", "audio/x-aiff");
+        defaultMap.put("wav", "audio/x-wav");
+        defaultMap.put("mpeg", "video/mpeg");
+        defaultMap.put("mpg", "video/mpeg");
+        defaultMap.put("mpe", "video/mpeg");
+        defaultMap.put("qt", "video/quicktime");
+        defaultMap.put("mov", "video/quicktime");
+        defaultMap.put("avi", "video/x-msvideo");
+        defaultMap.put("movie", "video/x-sgi-movie");
+        defaultMap.put("avx", "video/x-rad-screenplay");
+        defaultMap.put("wrl", "x-world/x-vrml");
+        defaultMap.put("mpv2", "video/mpeg2");
+        
+        /* Add XML related MIMEs */
+        
+        defaultMap.put("xml", "text/xml");
+        defaultMap.put("xsl", "text/xml");        
+        defaultMap.put("svg", "image/svg+xml");
+        defaultMap.put("svgz", "image/svg+xml");
+        defaultMap.put("wbmp", "image/vnd.wap.wbmp");
+        defaultMap.put("wml", "text/vnd.wap.wml");
+        defaultMap.put("wmlc", "application/vnd.wap.wmlc");
+        defaultMap.put("wmls", "text/vnd.wap.wmlscript");
+        defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
+    }
+    
+
+    private Hashtable<String,String> map = new Hashtable<String,String>();
+
+    public void addContentType(String extn, String type) {
+        map.put(extn, type.toLowerCase());
+    }
+
+    public Enumeration getExtensions() {
+        return map.keys();
+    }
+
+    public String getMimeType(String ext) {
+        return getContentTypeFor(ext);
+    }
+    
+    public String getContentType(String extn) {
+        String type = (String)map.get(extn.toLowerCase());
+        if( type == null ) type=(String)defaultMap.get( extn );
+        return type;
+    }
+
+    public void removeContentType(String extn) {
+        map.remove(extn.toLowerCase());
+    }
+
+    /** Get extension of file, without fragment id
+     */
+    public static String getExtension( String fileName ) {
+        // play it safe and get rid of any fragment id
+        // that might be there
+        int length=fileName.length();
+        
+        int newEnd = fileName.lastIndexOf('#');
+        if( newEnd== -1 ) newEnd=length;
+        // Instead of creating a new string.
+        //         if (i != -1) {
+        //             fileName = fileName.substring(0, i);
+        //         }
+        int i = fileName.lastIndexOf('.', newEnd );
+        if (i != -1) {
+             return  fileName.substring(i + 1, newEnd );
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+    
+    public String getContentTypeFor(String fileName) {
+        String extn=getExtension( fileName );
+        if (extn!=null) {
+            return getContentType(extn);
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+
+}
index df789c8..fba1b96 100644 (file)
@@ -57,7 +57,7 @@ public final class RequestUtil {
      */
     public static String encodeCookie(Cookie cookie) {
 
-        StringBuilder buf = new StringBuilder( cookie.getName() );
+        StringBuffer buf = new StringBuffer( cookie.getName() );
         buf.append("=");
         buf.append(cookie.getValue());
 
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java
new file mode 100644 (file)
index 0000000..0d52219
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.BitSet;
+
+/**
+ *
+ * This class is very similar to the java.net.URLEncoder class.
+ *
+ * Unfortunately, with java.net.URLEncoder there is no way to specify to the 
+ * java.net.URLEncoder which characters should NOT be encoded.
+ *
+ * This code was moved from DefaultServlet.java
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class URLEncoder {
+    protected static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'A', 'B', 'C', 'D', 'E', 'F'};
+
+    //Array containing the safe characters set.
+    protected BitSet safeCharacters = new BitSet(256);
+
+    public URLEncoder() {
+        for (char i = 'a'; i <= 'z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = 'A'; i <= 'Z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = '0'; i <= '9'; i++) {
+            addSafeCharacter(i);
+        }
+    }
+
+    public void addSafeCharacter( char c ) {
+       safeCharacters.set( c );
+    }
+
+    public String encode( String path ) {
+        int maxBytesPerChar = 10;
+        int caseDiff = ('a' - 'A');
+        StringBuffer rewrittenPath = new StringBuffer(path.length());
+        ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
+        OutputStreamWriter writer = null;
+        try {
+            writer = new OutputStreamWriter(buf, "UTF8");
+        } catch (Exception e) {
+            e.printStackTrace();
+            writer = new OutputStreamWriter(buf);
+        }
+
+        for (int i = 0; i < path.length(); i++) {
+            int c = (int) path.charAt(i);
+            if (safeCharacters.get(c)) {
+                rewrittenPath.append((char)c);
+            } else {
+                // convert to external encoding before hex conversion
+                try {
+                    writer.write((char)c);
+                    writer.flush();
+                } catch(IOException e) {
+                    buf.reset();
+                    continue;
+                }
+                byte[] ba = buf.toByteArray();
+                for (int j = 0; j < ba.length; j++) {
+                    // Converting each byte in the buffer
+                    byte toEncode = ba[j];
+                    rewrittenPath.append('%');
+                    int low = (int) (toEncode & 0x0f);
+                    int high = (int) ((toEncode & 0xf0) >> 4);
+                    rewrittenPath.append(hexadecimal[high]);
+                    rewrittenPath.append(hexadecimal[low]);
+                }
+                buf.reset();
+            }
+        }
+        return rewrittenPath.toString();
+    }
+}