Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / services / reflect / JarLoader.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/reflect/JarLoader.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/reflect/JarLoader.java
new file mode 100644 (file)
index 0000000..ea7cd9e
--- /dev/null
@@ -0,0 +1,543 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.services.reflect.JarLoader\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.services.reflect;\r
+\r
+import org.apache.derby.iapi.services.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+\r
+import java.security.AccessController;\r
+import java.security.CodeSource;\r
+import java.security.GeneralSecurityException;\r
+import java.security.PrivilegedActionException;\r
+import java.security.SecureClassLoader;\r
+import java.security.cert.Certificate;\r
+import java.security.cert.X509Certificate;\r
+import java.util.jar.JarEntry;\r
+import java.util.jar.JarFile;\r
+import java.util.jar.JarInputStream;\r
+\r
+import org.apache.derby.iapi.services.io.AccessibleByteArrayOutputStream;\r
+import org.apache.derby.iapi.services.io.InputStreamUtil;\r
+import org.apache.derby.iapi.services.io.LimitInputStream;\r
+import org.apache.derby.iapi.util.IdUtil;\r
+\r
+import org.apache.derby.iapi.reference.MessageId;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.io.StorageFile;\r
+\r
+\r
+final class JarLoader extends SecureClassLoader {\r
+    \r
+    /**\r
+     * Two part name for the jar file.\r
+     */\r
+    private final String[] name;\r
+    \r
+    /**\r
+     * Handle to the installed jar file.\r
+     */\r
+    private StorageFile installedJar;\r
+    \r
+    /**\r
+     * When the jar file can be manipulated as a java.util.JarFile\r
+     * this holds the reference to the open jar. When the jar can\r
+     * only be manipulated as an InputStream (because the jar is itself\r
+     * in a database jar) then this will be null.\r
+     */\r
+    private JarFile jar;\r
+    \r
+    /**\r
+     * True if the jar can only be accessed using a stream, because\r
+     * the jar is itself in a database jar. When fals the jar is accessed\r
+     * using the jar field.\r
+     */\r
+    private boolean isStream;\r
+\r
+       private UpdateLoader updateLoader;\r
+       private HeaderPrintWriter vs;\r
+\r
+       JarLoader(UpdateLoader updateLoader, String[] name, HeaderPrintWriter vs) {\r
+\r
+               this.updateLoader = updateLoader;\r
+        this.name = name;\r
+               this.vs = vs;\r
+       }\r
+\r
+       /**\r
+        *  Initialize the class loader so it knows if it\r
+        *  is loading from a ZipFile or an InputStream\r
+        */\r
+       void initialize() {\r
+\r
+               String schemaName = name[IdUtil.DBCP_SCHEMA_NAME];\r
+               String sqlName = name[IdUtil.DBCP_SQL_JAR_NAME];\r
+\r
+               Exception e;\r
+               try {\r
+                       installedJar =\r
+                               updateLoader.getJarReader().getJarFile(\r
+                                       schemaName, sqlName);\r
+\r
+                       if (installedJar instanceof File) {\r
+                try {\r
+                    jar = (JarFile) AccessController.doPrivileged\r
+                    (new java.security.PrivilegedExceptionAction(){\r
+\r
+                        public Object run() throws IOException {\r
+                        return new JarFile((File) installedJar);\r
+\r
+                        }\r
+\r
+                    }\r
+                     );\r
+                } catch (PrivilegedActionException pae) {\r
+                    throw (IOException) pae.getException();\r
+                }\r
+                               return;\r
+                       }\r
+\r
+                       // Jar is only accessible as an InputStream,\r
+                       // which means we need to re-open the stream for\r
+                       // each access.\r
+\r
+                       isStream = true;\r
+                       return;\r
+\r
+               } catch (IOException ioe) {\r
+                       e = ioe;\r
+               } catch (StandardException se) {\r
+                       e = se;\r
+               }\r
+\r
+               if (vs != null)\r
+                       vs.println(MessageService.getTextMessage(\r
+                                       MessageId.CM_LOAD_JAR_EXCEPTION, getJarName(), e));\r
+\r
+               // No such zip.\r
+               setInvalid();\r
+       }\r
+\r
+       /**\r
+        * Handle all requests to the top-level loader.\r
+        * \r
+        * @exception ClassNotFoundException\r
+        *                Class can not be found\r
+        */\r
+       protected Class loadClass(String className, boolean resolve) \r
+               throws ClassNotFoundException {\r
+        \r
+        // Classes in installed jars cannot reference\r
+        // Derby internal code. This is to avoid\r
+        // code in installed jars bypassing SQL\r
+        // authorization by calling Derby's internal methods.\r
+        //\r
+        // Any classes in the org.apache.derby.jdbc package\r
+        // are allowed as it allows routines to make JDBC\r
+        // connections to other databases. This does expose\r
+        // public classes in that package that are not part\r
+        // of the public api to attacks. One could attempt\r
+        // further limiting allowed classes to those starting\r
+        // with Embedded (and Client) but when fetching the\r
+        // default connection in a routine (jdbc:default:connection)\r
+        // the DriverManager attempts a load of the already loaded\r
+        // AutoloadDriver, I think to establish the calling class\r
+        // has access to the driver.\r
+        //\r
+        // This check in addition to the one in UpdateLoader\r
+        // that prevents restricted classes from being loaded\r
+        // from installed jars. The checks should be seen as\r
+        // independent, ie. the restricted load check should\r
+        // not make assumptions about this check reducing the\r
+        // number of classes it has to check for.\r
+        if (className.startsWith("org.apache.derby.")\r
+                && !className.startsWith("org.apache.derby.jdbc."))\r
+        {\r
+            ClassNotFoundException cnfe = new ClassNotFoundException(className);\r
+            //cnfe.printStackTrace(System.out);\r
+            throw cnfe;\r
+        }\r
+\r
+               // we attempt the system class load even if we\r
+               // are stale because otherwise we will fail\r
+               // to load java.* classes which confuses some VMs\r
+               try {\r
+                       return Class.forName(className);\r
+                       //Added by Jeff Huang\r
+                       //TODO: FIXIT\r
+               } catch (ClassNotFoundException cnfe) {\r
+\r
+                       if (updateLoader == null)\r
+                               throw new ClassNotFoundException(MessageService.getTextMessage(MessageId.CM_STALE_LOADER, className));\r
+\r
+                       Class c = updateLoader.loadClass(className, resolve);\r
+                       if (c == null)\r
+                               throw cnfe;\r
+                       return c;\r
+               }\r
+       }\r
+\r
+       /**\r
+               \r
+       */\r
+       public InputStream getResourceAsStream(String name) {\r
+               if (updateLoader == null)\r
+                       return null;\r
+               return updateLoader.getResourceAsStream(name);\r
+       }\r
+\r
+    /**\r
+     * Return the SQL name for the installed jar.\r
+     * Used for error and informational messages.\r
+     */\r
+    final String getJarName() {\r
+        return IdUtil.mkQualifiedName(name);\r
+    }\r
+\r
+       Class loadClassData(String className, String jvmClassName, boolean resolve) {\r
+\r
+               if (updateLoader == null)\r
+                       return null;\r
+\r
+               try {\r
+                       if (jar != null)\r
+                               return loadClassDataFromJar(className, jvmClassName, resolve);\r
+\r
+                       if (isStream) {\r
+                               // have to use a new stream each time\r
+                               return loadClassData(installedJar.getInputStream(),\r
+                                               className, jvmClassName, resolve);\r
+                       }\r
+\r
+                       return null;\r
+               } catch (FileNotFoundException fnfe) {\r
+                       // No such entry.\r
+                       return null;\r
+               } catch (IOException ioe) {\r
+                       if (vs != null)\r
+                               vs.println(MessageService.getTextMessage(MessageId.CM_CLASS_LOAD_EXCEPTION, className, getJarName(), ioe));\r
+                       return null;\r
+               }       \r
+       }\r
+\r
+       /**\r
+               Get an InputStream for the given resource.\r
+       */\r
+       InputStream getStream(String name) {\r
+\r
+               if (updateLoader == null)\r
+                       return null;\r
+     \r
+               if (jar != null)\r
+                       return getRawStream(name);\r
+\r
+               if (isStream) {\r
+                       try {\r
+                               return getRawStream(installedJar.getInputStream(), name);\r
+                       } catch (FileNotFoundException e) {\r
+                               // no such entry\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+\r
+       /*\r
+       ** Private api\r
+       */\r
+\r
+\r
+    /**\r
+     * Load the class data when the installed jar is accessible\r
+     * as a java.util.jarFile.\r
+     */\r
+       private Class loadClassDataFromJar(\r
+            String className, String jvmClassName, boolean resolve) \r
+               throws IOException {\r
+\r
+               JarEntry e = jar.getJarEntry(jvmClassName);\r
+               if (e == null)\r
+                       return null;\r
+\r
+               InputStream in = jar.getInputStream(e);\r
+\r
+               try {\r
+                       return loadClassData(e, in, className, resolve);\r
+               } finally {\r
+                       in.close();\r
+               }\r
+       }\r
+\r
+    /**\r
+     * Load the class data when the installed jar is accessible\r
+     * only as an input stream (the jar is itself in a database jar).\r
+     */\r
+       private Class loadClassData(\r
+               InputStream in, String className, String jvmClassName, boolean resolve) \r
+               throws IOException {\r
+\r
+        JarInputStream jarIn = new JarInputStream(in);\r
+\r
+               for (;;) {\r
+\r
+                       JarEntry e = jarIn.getNextJarEntry();\r
+                       if (e == null) {\r
+                               jarIn.close();\r
+                               return null;\r
+                       }\r
+\r
+                       if (e.getName().equals(jvmClassName)) {\r
+                               Class c = loadClassData(e, jarIn, className, resolve);\r
+                               jarIn.close();\r
+                               return c;\r
+                       }\r
+               }\r
+               \r
+       }\r
+\r
+    /**\r
+     * Load and optionally resolve the class given its\r
+     * JarEntry and an InputStream to the class fiel format.\r
+     * This is common code for when the jar is accessed\r
+     * directly using JarFile or through InputStream.\r
+     */\r
+       private Class loadClassData(JarEntry e, InputStream in,\r
+               String className, boolean resolve) throws IOException {\r
+\r
+               byte[] data = readData(e, in, className);\r
+\r
+               Certificate[] signers = getSigners(className, e);\r
+\r
+               synchronized (updateLoader) {\r
+                       // see if someone else loaded it while we\r
+                       // were getting the bytes ...\r
+                       Class c = updateLoader.checkLoaded(className, resolve);\r
+                       if (c == null) {\r
+                               c = defineClass(className, data, 0, data.length, (CodeSource) null);\r
+                               if (signers != null) {\r
+                                       setSigners(c, signers);\r
+                               }\r
+                               if (resolve)\r
+                                       resolveClass(c);\r
+                       }\r
+                       return c;\r
+\r
+               }\r
+       }\r
+\r
+       Class checkLoaded(String className, boolean resolve) {\r
+               if (updateLoader == null)\r
+                       return null;\r
+\r
+               Class c = findLoadedClass(className);\r
+               if ((c != null) && resolve)\r
+                       resolveClass(c);\r
+               return c;\r
+       }\r
+\r
+    /**\r
+     * Set this loader to be invaid so that it will not\r
+     * resolve any classes or resources.\r
+     *\r
+     */\r
+       void setInvalid() {\r
+               updateLoader = null;\r
+        if (jar != null) {\r
+            try {\r
+                jar.close();\r
+            } catch (IOException ioe) {\r
+            }\r
+            jar = null;\r
+\r
+        }\r
+        isStream = false;\r
+       }\r
+\r
+       /*\r
+       ** Routines to get an InputStream for a namedResource\r
+       */\r
+\r
+       /**\r
+               Get a stream for a resource directly from a JarFile.\r
+               In this case we can safely return the stream directly.\r
+               It's a new stream set up by the zip code to read just\r
+               the contents of this entry.\r
+       */\r
+       private InputStream getRawStream(String name) {\r
+\r
+               try {\r
+                       JarEntry e = jar.getJarEntry(name);\r
+                       if (e == null)\r
+                               return null;\r
+\r
+                       return jar.getInputStream(e);\r
+               } catch (IOException ioe) {\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+               Get a stream from a zip file that is itself a stream.\r
+        We copy to the contents to a byte array and return a\r
+        stream around that to the caller. Though a copy is\r
+        involved it has the benefit of:\r
+        <UL>\r
+        <LI> Isolating the application from the JarInputStream, thus\r
+        denying any possibility of the application reading more of the\r
+        jar that it should be allowed to. E.g. the contents class files are not\r
+        exposed through getResource.\r
+        <LI> Avoids any possibility of the application holding onto\r
+        the open stream beyond shutdown of the database, thus leading\r
+        to leaked file descriptors or inability to remove the jar.\r
+        </UL>\r
+       */\r
+       private InputStream getRawStream(InputStream in, String name) { \r
+\r
+               JarInputStream jarIn = null;\r
+               try {\r
+                       jarIn = new JarInputStream(in);\r
+\r
+                   JarEntry e;\r
+                       while ((e = jarIn.getNextJarEntry()) != null) {\r
+\r
+                               if (e.getName().equals(name)) {\r
+                    int size = (int) e.getSize();\r
+                    if (size == -1)\r
+                    {\r
+                        // unknown size so just pick a good buffer size.\r
+                        size = 8192;\r
+                    }\r
+                    return AccessibleByteArrayOutputStream.copyStream(jarIn, size);\r
+                               }\r
+                       }\r
+\r
+               } catch (IOException ioe) {\r
+            // can't read the jar file just assume it doesn't exist.\r
+               }\r
+        finally {\r
+            if (jarIn != null) {\r
+                try {\r
+                    jarIn.close();\r
+                } catch (IOException ioe2) {\r
+                }\r
+            }            \r
+        }\r
+               return null;\r
+       }\r
+    \r
+    /**\r
+     * Read the raw data for the class file format\r
+     * into a byte array that can be used for loading the class.\r
+     * If this is a signed class and it has been compromised then\r
+     * a SecurityException will be thrown.\r
+     */\r
+    byte[] readData(JarEntry ze, InputStream in, String className)\r
+            throws IOException {\r
+\r
+        try {\r
+            int size = (int) ze.getSize();\r
+\r
+            if (size != -1) {\r
+                byte[] data = new byte[size];\r
+\r
+                InputStreamUtil.readFully(in, data, 0, size);\r
+\r
+                return data;\r
+            }\r
+\r
+            // unknown size\r
+            byte[] data = new byte[1024];\r
+            ByteArrayOutputStream os = new ByteArrayOutputStream(1024);\r
+            int r;\r
+            while ((r = in.read(data)) != -1) {\r
+                os.write(data, 0, r);\r
+            }\r
+\r
+            data = os.toByteArray();\r
+            return data;\r
+        } catch (SecurityException se) {\r
+            throw handleException(se, className);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Validate the security certificates (signers) for the class data.\r
+     */\r
+    private Certificate[] getSigners(String className, JarEntry je) throws IOException {\r
+\r
+        try {\r
+            Certificate[] list = je.getCertificates();\r
+            if ((list == null) || (list.length == 0)) {\r
+                return null;\r
+            }\r
+\r
+            for (int i = 0; i < list.length; i++) {\r
+                if (!(list[i] instanceof X509Certificate)) {\r
+                    String msg = MessageService.getTextMessage(\r
+                            MessageId.CM_UNKNOWN_CERTIFICATE, className,\r
+                            getJarName());\r
+\r
+                    throw new SecurityException(msg);\r
+                }\r
+\r
+                X509Certificate cert = (X509Certificate) list[i];\r
+\r
+                cert.checkValidity();\r
+            }\r
+\r
+            return list;\r
+\r
+        } catch (GeneralSecurityException gse) {\r
+            // convert this into an unchecked security\r
+            // exception. Unchecked as eventually it has\r
+            // to pass through a method that's only throwing\r
+            // ClassNotFoundException\r
+            throw handleException(gse, className);\r
+        }\r
+        \r
+    }\r
+\r
+    /**\r
+     * Provide a SecurityManager with information about the class name\r
+     * and the jar file.\r
+     */\r
+    private SecurityException handleException(Exception e, String className) {\r
+        String msg = MessageService.getTextMessage(\r
+                MessageId.CM_SECURITY_EXCEPTION, className, getJarName(), e\r
+                        .getLocalizedMessage());\r
+        return new SecurityException(msg);\r
+    }\r
+    \r
+    /**\r
+     * Return the jar name if toString() is called\r
+     * on this class loader.\r
+     */\r
+    public String toString()\r
+    {\r
+        return getJarName() + ":" + super.toString();\r
+    }\r
+}\r