Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / execute / JarUtil.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/JarUtil.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/JarUtil.java
new file mode 100644 (file)
index 0000000..d59e294
--- /dev/null
@@ -0,0 +1,400 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.JarUtil\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.sql.execute;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.security.AccessController;\r
+import java.security.PrivilegedActionException;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.property.PropertyUtil;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.FileInfoDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.store.access.FileResource;\r
+import org.apache.derby.iapi.util.IdUtil;\r
+\r
+\r
+public class JarUtil\r
+{\r
+       //\r
+       //State passed in by the caller\r
+    private LanguageConnectionContext lcc;\r
+       private String schemaName;\r
+       private String sqlName;\r
+\r
+       //Derived state\r
+       \r
+       private FileResource fr;\r
+       private DataDictionary dd;\r
+       private DataDescriptorGenerator ddg;\r
+       \r
+       //\r
+       //State derived from the caller's context\r
+       private JarUtil(LanguageConnectionContext lcc,\r
+            String schemaName, String sqlName)\r
+                throws StandardException\r
+       {\r
+               this.schemaName = schemaName;\r
+               this.sqlName = sqlName;\r
+\r
+        this.lcc = lcc;\r
+               fr = lcc.getTransactionExecute().getFileHandler();\r
+               dd = lcc.getDataDictionary();\r
+               ddg = dd.getDataDescriptorGenerator();\r
+       }\r
+\r
+       /**\r
+         install a jar file to the current connection's database.\r
+\r
+         @param schemaName the name for the schema that holds the jar file.\r
+         @param sqlName the sql name for the jar file.\r
+         @param externalPath the path for the jar file to add.\r
+         @return The generationId for the jar file we add.\r
+\r
+         @exception StandardException Opps\r
+         */\r
+       public static long\r
+       install(LanguageConnectionContext lcc,\r
+            String schemaName, String sqlName, String externalPath)\r
+                throws StandardException\r
+       {\r
+               JarUtil jutil = new JarUtil(lcc, schemaName, sqlName);\r
+               InputStream is = null;\r
+               \r
+               try {\r
+                       is = openJarURL(externalPath);\r
+                       return jutil.add(is);\r
+               } catch (java.io.IOException fnfe) {\r
+                       throw StandardException.newException(SQLState.SQLJ_INVALID_JAR, fnfe, externalPath);\r
+               }\r
+               finally {\r
+                       try {if (is != null) is.close();}\r
+                       catch (IOException ioe) {}\r
+               }\r
+       }\r
+\r
+       /**\r
+         Add a jar file to the current connection's database.\r
+\r
+         <P> The reason for adding the jar file in this private instance\r
+         method is that it allows us to share set up logic with drop and\r
+         replace.\r
+         @param is A stream for reading the content of the file to add.\r
+         @exception StandardException Opps\r
+         */\r
+       private long add(final InputStream is) throws StandardException\r
+       {\r
+               //\r
+               //Like create table we say we are writing before we read the dd\r
+               dd.startWriting(lcc);\r
+               FileInfoDescriptor fid = getInfo();\r
+               if (fid != null)\r
+                       throw\r
+                               StandardException.newException(SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT, \r
+                                                                                          fid.getDescriptorType(), sqlName, fid.getSchemaDescriptor().getDescriptorType(), schemaName);\r
+\r
+        SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, null, true);\r
+        try {\r
+            notifyLoader(false);\r
+            dd.invalidateAllSPSPlans();\r
+            final String jarExternalName = JarUtil.mkExternalName(schemaName,\r
+                    sqlName, fr.getSeparatorChar());\r
+\r
+            long generationId = setJar(jarExternalName, is, true, 0L);\r
+\r
+            fid = ddg.newFileInfoDescriptor(/*DJD*/null, sd, sqlName, generationId);\r
+            dd.addDescriptor(fid, sd, DataDictionary.SYSFILES_CATALOG_NUM,\r
+                    false, lcc.getTransactionExecute());\r
+            return generationId;\r
+        } finally {\r
+            notifyLoader(true);\r
+        }\r
+       }\r
+\r
+       /**\r
+     * Drop a jar file from the current connection's database.\r
+     * \r
+     * @param schemaName\r
+     *            the name for the schema that holds the jar file.\r
+     * @param sqlName\r
+     *            the sql name for the jar file.\r
+     * \r
+     * @exception StandardException\r
+     *                Opps\r
+     */\r
+       public static void\r
+       drop(LanguageConnectionContext lcc, String schemaName, String sqlName)\r
+                throws StandardException\r
+       {\r
+               JarUtil jutil = new JarUtil(lcc, schemaName,sqlName);\r
+               jutil.drop();\r
+       }\r
+\r
+       /**\r
+         Drop a jar file from the current connection's database.\r
+\r
+         <P> The reason for dropping  the jar file in this private instance\r
+         method is that it allows us to share set up logic with add and\r
+         replace.\r
+\r
+         @exception StandardException Opps\r
+         */\r
+       private void drop() throws StandardException\r
+       {\r
+               //\r
+               //Like create table we say we are writing before we read the dd\r
+               dd.startWriting(lcc);\r
+               FileInfoDescriptor fid = getInfo();\r
+               if (fid == null)\r
+                       throw StandardException.newException(SQLState.LANG_FILE_DOES_NOT_EXIST, sqlName,schemaName);\r
+\r
+               String dbcp_s = PropertyUtil.getServiceProperty(lcc.getTransactionExecute(),Property.DATABASE_CLASSPATH);\r
+               if (dbcp_s != null)\r
+               {\r
+                       String[][]dbcp= IdUtil.parseDbClassPath(dbcp_s);\r
+                       boolean found = false;\r
+                       //\r
+                       //Look for the jar we are dropping on our database classpath.\r
+                       //We don't concern ourselves with 3 part names since they may\r
+                       //refer to a jar file in another database and may not occur in\r
+                       //a database classpath that is stored in the propert congomerate.\r
+                       for (int ix=0;ix<dbcp.length;ix++)\r
+                               if (dbcp.length == 2 &&\r
+                                       dbcp[ix][0].equals(schemaName) && dbcp[ix][1].equals(sqlName))\r
+                                       found = true;\r
+                       if (found)\r
+                               throw StandardException.newException(SQLState.LANG_CANT_DROP_JAR_ON_DB_CLASS_PATH_DURING_EXECUTION, \r
+                                                                       IdUtil.mkQualifiedName(schemaName,sqlName),\r
+                                                                       dbcp_s);\r
+               }\r
+\r
+               try {\r
+               \r
+                       notifyLoader(false);\r
+                       dd.invalidateAllSPSPlans();\r
+                       DependencyManager dm = dd.getDependencyManager();\r
+                       dm.invalidateFor(fid, DependencyManager.DROP_JAR, lcc);\r
+\r
+                       dd.dropFileInfoDescriptor(fid);\r
+\r
+                       fr.remove(JarUtil.mkExternalName(schemaName, sqlName, fr.getSeparatorChar()),\r
+                               fid.getGenerationId());\r
+               } finally {\r
+                       notifyLoader(true);\r
+               }\r
+       }\r
+\r
+       /**\r
+         Replace a jar file from the current connection's database with the content of an\r
+         external file. \r
+\r
+\r
+         @param schemaName the name for the schema that holds the jar file.\r
+         @param sqlName the sql name for the jar file.\r
+         @param externalPath the path for the jar file to add.\r
+         @return The new generationId for the jar file we replace.\r
+\r
+         @exception StandardException Opps\r
+         */\r
+       public static long\r
+       replace(LanguageConnectionContext lcc, String schemaName, String sqlName,\r
+                       String externalPath)\r
+                throws StandardException\r
+       {\r
+               JarUtil jutil = new JarUtil(lcc, schemaName,sqlName);\r
+               InputStream is = null;\r
+               \r
+\r
+               try {\r
+                       is = openJarURL(externalPath);\r
+\r
+                       return jutil.replace(is);\r
+               } catch (java.io.IOException fnfe) {\r
+                       throw StandardException.newException(SQLState.SQLJ_INVALID_JAR, fnfe, externalPath);\r
+               }\r
+               finally {\r
+                       try {if (is != null) is.close();}\r
+                       catch (IOException ioe) {}\r
+               }\r
+       }\r
+\r
+       /**\r
+         Replace a jar file in the current connection's database with the\r
+         content of an external file.\r
+\r
+         <P> The reason for adding the jar file in this private instance\r
+         method is that it allows us to share set up logic with add and\r
+         drop.\r
+         @param is An input stream for reading the new content of the jar file.\r
+         @exception StandardException Opps\r
+         */\r
+       private long replace(InputStream is) throws StandardException\r
+       {\r
+               //\r
+               //Like create table we say we are writing before we read the dd\r
+               dd.startWriting(lcc);\r
+\r
+               //\r
+               //Temporarily drop the FileInfoDescriptor from the data dictionary.\r
+               FileInfoDescriptor fid = getInfo();\r
+               if (fid == null)\r
+                       throw StandardException.newException(SQLState.LANG_FILE_DOES_NOT_EXIST, sqlName,schemaName);\r
+\r
+               try {\r
+                       // disable loads from this jar\r
+                       notifyLoader(false);\r
+                       dd.invalidateAllSPSPlans();\r
+                       dd.dropFileInfoDescriptor(fid);\r
+            final String jarExternalName =\r
+                JarUtil.mkExternalName(schemaName, sqlName, fr.getSeparatorChar());\r
+\r
+                       //\r
+                       //Replace the file.\r
+                       long generationId = setJar(jarExternalName, is, false,\r
+                                       fid.getGenerationId());\r
+            \r
+                       //\r
+                       //Re-add the descriptor to the data dictionary.\r
+                       FileInfoDescriptor fid2 = \r
+                               ddg.newFileInfoDescriptor(fid.getUUID(),fid.getSchemaDescriptor(),\r
+                                                               sqlName,generationId);\r
+                       dd.addDescriptor(fid2, fid.getSchemaDescriptor(),\r
+                                                        DataDictionary.SYSFILES_CATALOG_NUM, false, lcc.getTransactionExecute());\r
+                       return generationId;\r
+\r
+               } finally {\r
+\r
+                       // reenable class loading from this jar\r
+                       notifyLoader(true);\r
+               }\r
+       }\r
+\r
+       /**\r
+         Get the FileInfoDescriptor for the Jar file or null if it does not exist.\r
+         @exception StandardException Ooops\r
+         */\r
+       private FileInfoDescriptor getInfo()\r
+                throws StandardException\r
+       {\r
+               SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, null, true);\r
+               return dd.getFileInfoDescriptor(sd,sqlName);\r
+       }\r
+\r
+       private void notifyLoader(boolean reload) throws StandardException {\r
+               ClassFactory cf = lcc.getLanguageConnectionFactory().getClassFactory();\r
+               cf.notifyModifyJar(reload);\r
+       }\r
+\r
+    /**\r
+     * Open an input stream to read a URL or a file.\r
+     * URL is attempted first, if the string does not conform\r
+     * to a URL then an attempt to open it as a regular file\r
+     * is tried.\r
+     * <BR>\r
+     * Attempting the file first can throw a security execption\r
+     * when a valid URL is passed in.\r
+     * The security exception is due to not have the correct permissions\r
+     * to access the bogus file path. To avoid this the order was reversed\r
+     * to attempt the URL first and only attempt a file open if creating\r
+     * the URL throws a MalformedURLException.\r
+     */\r
+    private static InputStream openJarURL(final String externalPath)\r
+        throws IOException\r
+    {\r
+        try {\r
+            return (InputStream) AccessController.doPrivileged\r
+            (new java.security.PrivilegedExceptionAction(){\r
+                \r
+                public Object run() throws IOException {    \r
+                    try {\r
+                        return new URL(externalPath).openStream();\r
+                    } catch (MalformedURLException mfurle)\r
+                    {\r
+                        return new FileInputStream(externalPath);\r
+                    }\r
+                }\r
+            });\r
+        } catch (PrivilegedActionException e) {\r
+            throw (IOException) e.getException();\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Copy the jar from the externally obtained \r
+     * input stream into the database\r
+     * @param jarExternalName Name of jar with database structure.\r
+     * @param contents Contents of jar file.\r
+     * @param add true to add, false to replace\r
+     * @param currentGenerationId generation id of existing version, ignored when adding.\r
+     */\r
+    private long setJar(final String jarExternalName,\r
+            final InputStream contents,\r
+            final boolean add,\r
+            final long currentGenerationId)\r
+            throws StandardException {\r
+        try {\r
+            return ((Long) AccessController\r
+                    .doPrivileged(new java.security.PrivilegedExceptionAction() {\r
+\r
+                        public Object run() throws StandardException {\r
+                            long generationId;\r
+                            \r
+                            if (add)\r
+                                generationId = fr.add(jarExternalName, contents);\r
+                            else\r
+                                generationId =  fr.replace(jarExternalName,\r
+                                        currentGenerationId, contents);\r
+                            return new Long(generationId);\r
+                        }\r
+                    })).longValue();\r
+        } catch (PrivilegedActionException e) {\r
+            throw (StandardException) e.getException();\r
+        }\r
+    }\r
+    \r
+    /**\r
+      Make an external name for a jar file stored in the database.\r
+      */\r
+    public static String mkExternalName(String schemaName, String sqlName, char separatorChar)\r
+    {\r
+        StringBuffer sb = new StringBuffer(30);\r
+\r
+        sb.append(FileResource.JAR_DIRECTORY_NAME);\r
+        sb.append(separatorChar);\r
+        sb.append(schemaName);\r
+        sb.append(separatorChar);\r
+        sb.append(sqlName);\r
+        sb.append(".jar");\r
+        return sb.toString();\r
+    }\r
+}\r