blob: 9076671ab133d2cc9f0f6407085b418f394b9611 [file] [log] [blame]
/*
* 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.
*/
/*
* Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden
*
* Distributed under the terms shown in the file COPYRIGHT
* found in the root folder of this project or at
* http://eng.tada.se/osprojects/COPYRIGHT.html
*/
package org.postgresql.pljava.sqlj;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.postgresql.pljava.internal.Backend;
import org.postgresql.pljava.internal.Oid;
import org.postgresql.pljava.jdbc.SQLUtils;
import org.postgresql.pljava.sqlj.JarLoader;
/**
*
* The Loader class is used by PL/Java to load java bytecode into the VM.
*
* In the postgres distribution this bytecode is always loaded from the
* sqlj.jar_repository table.
*
* In Greenplum that is difficult because it would require uniform access
* to the sqlj.jar_repository table from every segment, which is currently
* not well supported by our architecture.
*
* Instead we mostly use our own loader JarLoader() which reads a jarfile
* from the filesystem. Note that the security manager allows READING from
* "$GPHOME/lib/postgresql/java/*.class" files, even in trusted mode, since
* this is shared between all databases any user can theoretically see every
* installed jar.
*
* Note: This creates a couple differences from the postgres implementation that
* currently pull us further from the sql standard embedded java implementation.
* Specifically:
* - We do not support per-database/per-schema CLASSPATH
* - CLASSPATH is instead set by a user GUC.
*/
public class Loader extends ClassLoader
{
private static final String PUBLIC_SCHEMA = "public";
private static final Map s_schemaLoaders = new HashMap();
private static final Map s_typeMap = new HashMap();
private final static Logger s_logger = Logger.getLogger(Loader.class.getName());
/* Greeplum Additions */
private final LinkedList m_jarloaders;
private final String[] m_classpath;
// private final Map m_filespace; // Not yet supported
private static String m_current_classpath;
/**
* Create a new Loader.
* @param entries
* @param parent
*/
Loader(String classpath)
throws SQLException
{
super(Loader.class.getClassLoader());
m_jarloaders = new LinkedList();
if (classpath == null || classpath.length() == 0)
m_classpath = new String[0];
else
m_classpath = classpath.split(":");
/* Find the directory that contains our jar files */
String jarpath = Backend.getLibraryPath() + "/java";
/* Create a new JarLoader for every element in the classpath */
for (int i = 0; i < m_classpath.length; i++)
{
try
{
URL url = new URL("file:///" + jarpath + "/" + m_classpath[i]);
JarLoader loader = new JarLoader(this, url);
m_jarloaders.add(loader);
}
catch (MalformedURLException e)
{
// XXX - Ignore malformed URLs?
throw new SQLException("Malformed URL Exception: " +
e.getMessage());
}
catch (IOException e)
{
// XXX - Ignore jar doesn't exist?
throw new SQLException("IOException reading jar: " +
e.getMessage());
}
}
}
/**
* Removes all cached schema loaders, functions, and type maps. This
* method is called by the utility functions that manipulate the
* data that has been cached. It is not intended to be called
* from user code.
*/
public static void clearSchemaLoaders()
{
s_schemaLoaders.clear();
s_typeMap.clear();
Backend.clearFunctionCache();
}
/**
* Obtains the loader that is in effect for the current schema (i.e. the
* schema that is first in the search path).
* @return A loader
* @throws SQLException
*/
public static ClassLoader getCurrentLoader()
throws SQLException
{
/*
* Because Greenplum doesn't support per-schema classpaths we just map
* everything to the public schema.
*/
return getSchemaLoader(PUBLIC_SCHEMA);
}
/**
* Obtain a loader that has been configured for the class path of the
* schema named <code>schemaName</code>. Class paths are defined using the
* SQL procedure <code>sqlj.set_classpath</code>.
* @param schemaName The name of the schema.
* @return A loader.
*/
public static ClassLoader getSchemaLoader(String schemaName)
throws SQLException
{
/*
* Rather than having a different loader per schemaName, instead
* we simply create a different loader per CLASSPATH.
*/
String classpath = Backend.getConfigOption("pljava_classpath");
if (classpath == null)
classpath = "";
if (!classpath.equals(m_current_classpath))
{
clearSchemaLoaders();
m_current_classpath = classpath;
}
ClassLoader loader = (ClassLoader) s_schemaLoaders.get(classpath);
if (loader == null)
{
loader = (ClassLoader) new Loader(classpath);
s_schemaLoaders.put(classpath, loader);
}
return loader;
}
/**
* Returns the SQL type {@link Oid} to Java {@link Class} map that contains the
* Java UDT mappings for the given <code>schema</code>.
* This method is called by the function mapping mechanisms. Application code
* should never call this method.
*
* @param schema The schema
* @return The Map, possibly empty but never <code>null</code>.
*/
public static Map getTypeMap(final String schema)
throws SQLException
{
/* XXX - needs implementation to support TypeMaps */
return Collections.EMPTY_MAP;
/*
Map typesForSchema = (Map)s_typeMap.get(schema);
if(typesForSchema != null)
return typesForSchema;
s_logger.fine("Creating typeMappings for schema " + schema);
typesForSchema = new HashMap()
{
public Object get(Object key)
{
s_logger.fine("Obtaining type mapping for OID " + key + " for schema " + schema);
return super.get(key);
}
};
ClassLoader loader = Loader.getSchemaLoader(schema);
Statement stmt = SQLUtils.getDefaultConnection().createStatement();
ResultSet rs = null;
try
{
rs = stmt.executeQuery("SELECT javaName, sqlName FROM sqlj.typemap_entry");
while(rs.next())
{
try
{
String javaClassName = rs.getString(1);
String sqlName = rs.getString(2);
Class cls = loader.loadClass(javaClassName);
if(!SQLData.class.isAssignableFrom(cls))
throw new SQLException("Class " + javaClassName + " does not implement java.sql.SQLData");
Oid typeOid = Oid.forTypeName(sqlName);
typesForSchema.put(typeOid, cls);
s_logger.fine("Adding type mapping for OID " + typeOid + " -> class " + cls.getName() + " for schema " + schema);
}
catch(ClassNotFoundException e)
{
// Ignore, type is not know to this schema and that is ok
}
}
if(typesForSchema.isEmpty())
typesForSchema = Collections.EMPTY_MAP;
s_typeMap.put(schema, typesForSchema);
return typesForSchema;
}
finally
{
SQLUtils.close(rs);
SQLUtils.close(stmt);
}
*/
}
protected Class findClass(final String name)
throws ClassNotFoundException
{
// Scan through all jar files in the classpath
for (ListIterator iter = m_jarloaders.listIterator(); iter.hasNext(); )
{
JarLoader loader = (JarLoader) iter.next();
try
{
Class r = loader.findClass(name);
if (r != null)
return r;
}
catch (ClassNotFoundException e)
{
// Ignore exception, look in other loaders (JAR files)
}
}
throw new ClassNotFoundException(name);
}
protected URL findResource(String name)
{
// Scan through all jar files in the classpath
for (ListIterator iter = m_jarloaders.listIterator(); iter.hasNext();)
{
JarLoader loader = (JarLoader) iter.next();
URL url = loader.findResource(name);
if (url != null)
return url;
}
return null;
}
}