Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / services / reflect / UpdateLoader.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/reflect/UpdateLoader.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/services/reflect/UpdateLoader.java
new file mode 100644 (file)
index 0000000..c4412d7
--- /dev/null
@@ -0,0 +1,427 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.services.reflect.UpdateLoader\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.context.ContextService;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.services.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.util.IdUtil;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.locks.ShExLockable;\r
+import org.apache.derby.iapi.services.locks.ShExQual;\r
+import org.apache.derby.iapi.services.locks.LockFactory;\r
+import org.apache.derby.iapi.services.locks.Latch;\r
+import org.apache.derby.iapi.services.locks.C_LockFactory;\r
+import org.apache.derby.iapi.services.loader.ClassFactoryContext;\r
+import org.apache.derby.iapi.services.loader.JarReader;\r
+import org.apache.derby.iapi.services.property.PersistentSet;\r
+\r
+import org.apache.derby.iapi.services.property.PropertyUtil;\r
+import org.apache.derby.iapi.reference.Property;\r
+\r
+import java.io.InputStream;\r
+import java.security.AccessController;\r
+\r
+import org.apache.derby.iapi.reference.MessageId;\r
+import org.apache.derby.iapi.reference.Module;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.locks.CompatibilitySpace;\r
+\r
+/**\r
+ * UpdateLoader implements then functionality of\r
+ * derby.database.classpath. It manages the ClassLoaders\r
+ * (instances of JarLoader) for each installed jar file.\r
+ * Jar files are installed through the sqlj.install_jar procedure.\r
+ * <BR>\r
+ * Each JarLoader delegates any request through standard mechanisms\r
+ * to load a class to this object, which will then ask each jarLoader in order of\r
+ * derby.database.classpath to load the class through an internal api.\r
+ * This means if the third jar in derby.database.classpath tries to load\r
+ * a class, say from the class for a procedure's method making some\r
+ * reference to it, then the request is delegated to UpdateLoader.\r
+ * UpdateLoader will then try to load the class from each of the jars\r
+ * in order of derby.database.classpath using the jar's installed JarLoader.\r
+ */\r
+final class UpdateLoader {\r
+    \r
+    /**\r
+     * List of packages that Derby will not support being loaded\r
+     * from an installed jar file.\r
+     */\r
+    private static final String[] RESTRICTED_PACKAGES = {\r
+        // While loading java. classes is blocked by the standard\r
+        // class loading mechanism, javax. ones are not. However\r
+        // allowing database applications to override jvm classes\r
+        // seems a bad idea.\r
+        "javax.",\r
+        \r
+        // Allowing an application to possible override the engine's\r
+        // own classes also seems dangerous.\r
+        "org.apache.derby.",\r
+    };\r
+\r
+       private JarLoader[] jarList;\r
+       private HeaderPrintWriter vs;\r
+       private final ClassLoader myLoader;\r
+       private boolean initDone;\r
+       private String thisClasspath;\r
+       private final LockFactory lf;\r
+       private final ShExLockable classLoaderLock;\r
+       private int version;\r
+    private boolean normalizeToUpper;\r
+       private DatabaseClasses parent;\r
+       private final CompatibilitySpace compat;\r
+\r
+       private boolean needReload;\r
+       private JarReader jarReader;\r
+\r
+       UpdateLoader(String classpath, DatabaseClasses parent, boolean verbose, boolean normalizeToUpper) \r
+               throws StandardException {\r
+\r
+        this.normalizeToUpper = normalizeToUpper;\r
+               this.parent = parent;\r
+               lf = (LockFactory) Monitor.getServiceModule(parent, Module.LockFactory);\r
+               compat = lf.createCompatibilitySpace(this);\r
+\r
+               if (verbose) {\r
+                       vs = Monitor.getStream();\r
+               }\r
+               \r
+               myLoader = getClass().getClassLoader();\r
+\r
+               this.classLoaderLock = new ClassLoaderLock(this);\r
+\r
+               initializeFromClassPath(classpath);\r
+       }\r
+\r
+       private void initializeFromClassPath(String classpath) throws StandardException {\r
+\r
+               final String[][] elements = IdUtil.parseDbClassPath(classpath);\r
+               \r
+               final int jarCount = elements.length;\r
+               jarList = new JarLoader[jarCount];\r
+                       \r
+        if (jarCount != 0) {\r
+            // Creating class loaders is a restricted operation\r
+            // so we need to use a privileged block.\r
+            AccessController.doPrivileged\r
+            (new java.security.PrivilegedAction(){\r
+                \r
+                public Object run(){    \r
+                     for (int i = 0; i < jarCount; i++) {\r
+                            jarList[i] = new JarLoader(UpdateLoader.this, elements[i], vs);\r
+                     }\r
+                  return null;\r
+                }\r
+            });\r
+        }\r
+               if (vs != null) {\r
+                       vs.println(MessageService.getTextMessage(MessageId.CM_CLASS_LOADER_START, classpath));\r
+               }\r
+               \r
+               thisClasspath = classpath;\r
+               initDone = false;\r
+       }\r
+\r
+       /**\r
+               Load the class from the class path. Called by JarLoader\r
+        when it has a request to load a class to fulfill\r
+        the sematics of derby.database.classpath.\r
+        <P>\r
+        Enforces two restrictions:\r
+        <UL>\r
+        <LI> Do not allow classes in certain name spaces to be loaded\r
+        from installed jars, see RESTRICTED_PACKAGES for the list.\r
+        <LI> Referencing Derby's internal classes (those outside the\r
+        public api) from installed is disallowed. This is to stop\r
+        user defined routines bypassing security or taking advantage\r
+        of security holes in Derby. E.g. allowing a routine to\r
+        call a public method in derby would allow such routines\r
+        to call public static methods for system procedures without\r
+        having been granted permission on them, such as setting database\r
+        properties.\r
+        </UL>\r
+\r
+               @exception ClassNotFoundException Class can not be found or\r
+        the installed jar is restricted from loading it.\r
+       */\r
+       Class loadClass(String className, boolean resolve) \r
+               throws ClassNotFoundException {\r
+\r
+               JarLoader jl = null;\r
+\r
+               boolean unlockLoader = false;\r
+               try {\r
+                       unlockLoader = lockClassLoader(ShExQual.SH);\r
+\r
+                       synchronized (this) {\r
+\r
+                               if (needReload) {\r
+                                       reload();\r
+                               }\r
+                       \r
+                               Class clazz = checkLoaded(className, resolve);\r
+                               if (clazz != null)\r
+                                       return clazz;\r
+                \r
+                // Refuse to load classes from restricted name spaces\r
+                // That is classes in those name spaces can be not\r
+                // loaded from installed jar files.\r
+                for (int i = 0; i < RESTRICTED_PACKAGES.length; i++)\r
+                {\r
+                    if (className.startsWith(RESTRICTED_PACKAGES[i]))\r
+                        throw new ClassNotFoundException(className);\r
+                }\r
+\r
+                               String jvmClassName = className.replace('.', '/').concat(".class");\r
+\r
+                               if (!initDone)\r
+                                       initLoaders();\r
+\r
+                               for (int i = 0; i < jarList.length; i++) {\r
+\r
+                                       jl = jarList[i];\r
+\r
+                                       Class c = jl.loadClassData(className, jvmClassName, resolve);\r
+                                       if (c != null) {\r
+                                               if (vs != null)\r
+                                                       vs.println(MessageService.getTextMessage(MessageId.CM_CLASS_LOAD, className, jl.getJarName()));\r
+\r
+                                               return c;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+\r
+\r
+               } catch (StandardException se) {\r
+                       throw new ClassNotFoundException(MessageService.getTextMessage(MessageId.CM_CLASS_LOAD_EXCEPTION, className, jl == null ? null : jl.getJarName(), se));\r
+               } finally {\r
+                       if (unlockLoader) {\r
+                               lf.unlock(compat, this, classLoaderLock, ShExQual.SH);\r
+                       }\r
+               }\r
+       }\r
+\r
+       InputStream getResourceAsStream(String name) {\r
+\r
+               InputStream is = (myLoader == null) ?\r
+                       ClassLoader.getSystemResourceAsStream(name) :\r
+                       myLoader.getResourceAsStream(name);\r
+\r
+               if (is != null)\r
+                       return is;\r
+\r
+               // match behaviour of standard class loaders. \r
+               if (name.endsWith(".class"))\r
+                       return null;\r
+\r
+               boolean unlockLoader = false;\r
+               try {\r
+                       unlockLoader = lockClassLoader(ShExQual.SH);\r
+\r
+                       synchronized (this) {\r
+\r
+                               if (needReload) {\r
+                                       reload();               \r
+                               }\r
+\r
+                               if (!initDone)\r
+                                       initLoaders();\r
+\r
+                               for (int i = 0; i < jarList.length; i++) {\r
+\r
+                                       JarLoader jl = jarList[i];\r
+\r
+                                       is = jl.getStream(name);\r
+                                       if (is != null) {\r
+                                               return is;\r
+                                       }\r
+                               }\r
+                       }\r
+                       return null;\r
+\r
+               } catch (StandardException se) {\r
+                       return null;\r
+               } finally {\r
+                       if (unlockLoader) {\r
+                               lf.unlock(compat, this, classLoaderLock, ShExQual.SH);\r
+                       }\r
+               }\r
+       }\r
+\r
+       synchronized void modifyClasspath(String classpath)\r
+               throws StandardException {\r
+\r
+               // lock transaction classloader exclusively\r
+               lockClassLoader(ShExQual.EX);\r
+               version++;\r
+\r
+\r
+               modifyJar(false);\r
+               initializeFromClassPath(classpath);\r
+       }\r
+\r
+\r
+       synchronized void modifyJar(boolean reload) throws StandardException {\r
+\r
+               // lock transaction classloader exclusively\r
+               lockClassLoader(ShExQual.EX);\r
+               version++;\r
+\r
+               if (!initDone)\r
+                       return;\r
+        \r
+        // first close the existing jar file opens\r
+        close();\r
+\r
+               if (reload) {\r
+                       initializeFromClassPath(thisClasspath);\r
+               }\r
+       }\r
+\r
+       private boolean lockClassLoader(ShExQual qualifier)\r
+               throws StandardException {\r
+\r
+               if (lf == null)\r
+                       return false;\r
+\r
+               ClassFactoryContext cfc = (ClassFactoryContext) ContextService.getContextOrNull(ClassFactoryContext.CONTEXT_ID);\r
+\r
+               // This method can be called from outside of the database\r
+               // engine, in which case tc will be null. In that case\r
+               // we lock the class loader only for the duration of\r
+               // the loadClass().\r
+               CompatibilitySpace lockSpace = null;\r
+               \r
+               if (cfc != null) {\r
+                       lockSpace = cfc.getLockSpace();\r
+               }\r
+               if (lockSpace == null)\r
+                       lockSpace = compat;\r
+\r
+               Object lockGroup = lockSpace.getOwner();\r
+\r
+               lf.lockObject(lockSpace, lockGroup, classLoaderLock, qualifier,\r
+                                         C_LockFactory.TIMED_WAIT);\r
+\r
+               return (lockGroup == this);\r
+       }\r
+\r
+       Class checkLoaded(String className, boolean resolve) {\r
+\r
+               for (int i = 0; i < jarList.length; i++) {\r
+                       Class c = jarList[i].checkLoaded(className, resolve);\r
+                       if (c != null)\r
+                               return c;\r
+               }\r
+               return null;\r
+       }\r
+\r
+       void close() {\r
+\r
+               for (int i = 0; i < jarList.length; i++) {\r
+                       jarList[i].setInvalid();\r
+               }\r
+\r
+       }\r
+\r
+       private void initLoaders() {\r
+\r
+               if (initDone)\r
+                       return;\r
+\r
+               for (int i = 0; i < jarList.length; i++) {\r
+                       jarList[i].initialize();\r
+               }\r
+               initDone = true;\r
+       }\r
+\r
+       int getClassLoaderVersion() {\r
+               return version;\r
+       }\r
+\r
+       synchronized void needReload() {\r
+               version++;\r
+               needReload = true;\r
+       }\r
+\r
+       private void reload() throws StandardException {\r
+               thisClasspath = getClasspath();\r
+               // first close the existing jar file opens\r
+               close();\r
+               initializeFromClassPath(thisClasspath);\r
+               needReload = false;\r
+       }\r
+\r
+\r
+       private String getClasspath()\r
+               throws StandardException {\r
+\r
+               ClassFactoryContext cfc = (ClassFactoryContext) ContextService.getContextOrNull(ClassFactoryContext.CONTEXT_ID);\r
+\r
+               PersistentSet ps = cfc.getPersistentSet();\r
+               \r
+               String classpath = PropertyUtil.getServiceProperty(ps, Property.DATABASE_CLASSPATH);\r
+\r
+               //\r
+               //In per database mode we must always have a classpath. If we do not\r
+               //yet have one we make one up.\r
+               if (classpath==null)\r
+                       classpath="";\r
+\r
+\r
+               return classpath;\r
+       }\r
+\r
+       JarReader getJarReader() {\r
+               if (jarReader == null) {\r
+\r
+                       ClassFactoryContext cfc = (ClassFactoryContext) ContextService.getContextOrNull(ClassFactoryContext.CONTEXT_ID);\r
+\r
+                       jarReader = cfc.getJarReader(); \r
+               }\r
+               return jarReader;\r
+       }\r
+}\r
+\r
+\r
+class ClassLoaderLock extends ShExLockable {\r
+\r
+       private UpdateLoader myLoader;\r
+\r
+       ClassLoaderLock(UpdateLoader myLoader) {\r
+               this.myLoader = myLoader;\r
+       }\r
+\r
+       public void unlockEvent(Latch lockInfo)\r
+       {\r
+               super.unlockEvent(lockInfo);\r
+\r
+               if (lockInfo.getQualifier().equals(ShExQual.EX)) {\r
+                       // how do we tell if we are reverting or not\r
+                       myLoader.needReload();\r
+               }\r
+       }\r
+}\r