开发者

JDBC Lotus-Oracle error: Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1

开发者 https://www.devze.com 2023-03-30 04:41 出处:网络
I am using jdbc to connect to an Oracle 10g database. Building the connection in Eclipse/Java works fine. However when I move the code to a Lotus 8.5.2 agent I end up with the following error(s):

I am using jdbc to connect to an Oracle 10g database. Building the connection in Eclipse/Java works fine. However when I move the code to a Lotus 8.5.2 agent I end up with the following error(s):

Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1
 at oracle.jdbc.driver.T4CTTIoauthenticate.setSessionFields(T4CTTIoauthenticate.java:1019)
 at oracle.jdbc.driver.T4CTTIoauthenticate.<init>(T4CTTIoauthenticate.java:186)
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:354)
 at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:454)
 at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:165)
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:35)
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:802)
 at oracle.jdbc.pool.OracleDataSource.getPhysicalConnection(OracleDataSource.java:298)
 at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:222)
 at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:166)
 at JavaAgent.NotesMain(Unknown Source)
 at lotus.domino.AgentBase.runNotes(Unknown Source)
 at lotus.domino.NotesThread.run(Unknown Source)

This is the code used to connect:

Class.forName("oracle.jdbc.OracleDr开发者_如何学编程iver");
Connection conn = DriverManager.getConnection(
 "jdbc:oracle:thin:@:xx.xx.xx.xx:1521:xx", "xx", "xx");

I have tried to solve this in different ways: - use the Lotus JVM in eclipse - use different jdbc jars in eclipse - use different ways to build the connection in Lotus - use different jdbc jars jars in lotus

Finally I moved the ojdbc14.jar file Lotus\Notes\jvm\lib\ext directory and it works fine now. This solution will work, but obviously I prefer to distribute this jar along with the nsf. Is there a way I can make this happen?

As suggested by leyrer. I tried adding the following line to the "/jvm/lib/security/java.policy" file

permission java.security.AllPermission;

This does results in the same error message. For now I will stick with placing the ojdbc5.jar in the /ext directory.


If you are using the ojdbc jar unpacked, be sure you are not excluding the oracle/sql/converter_xcharset/*.glb files. I was getting this same error when my executable jar was built using Maven, but was not including these files. The block below explicitly includes them.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>unpack-dependencies</id>
      <phase>prepare-package</phase>
      <goals>
        <goal>unpack-dependencies</goal>
      </goals>
      <configuration>
        <excludeTypes>pom</excludeTypes>
        <includes>**/*.class,**/*.glb</includes>
        <outputDirectory>${project.build.directory}/classes</outputDirectory>
        <overWriteReleases>false</overWriteReleases>
        <overWriteSnapshots>true</overWriteSnapshots>
      </configuration>
    </execution>
  </executions>
</plugin>


I would guess, that the JVM's Security Manager is not allowing access to the network because the security policy does not specify to allow this action. See Flying Saucer in Lotus Notes for more details.


Long time ago this issue arise... But I had to discover it for a customer this month. And nowhere I found a solution but some false pretences and incomplete analyses. Therefore and for all of you run into it, I will share my findings, the root cause of the issue and your opportunities to get it solved. I have tested it with the version 11.2.0.4 of the driver (ojdbc6.jar). Further, if your database uses UTF-8 encoding, it seems to work just with the java.policy adjustments. In my case, it was a database with windows 1252 encoding.

First of all, the oracle jdbc driver needs some security adjustments... it's better to set them explicitly and not by permission java.security.AllPermission;. Use this permissions, taken from the ocacle jdbc driver download page (ojdbc.policy file):

permission java.util.PropertyPermission "user.name", "read";
permission java.util.PropertyPermission "oracle.jdbc.*", "read";
permission java.util.PropertyPermission "oracle.net.wallet_location", "read";
permission java.util.PropertyPermission "oracle.net.tns_admin", "read";
permission javax.management.MBeanServerPermission "createMBeanServer";
permission javax.management.MBeanPermission "oracle.jdbc.driver.OracleDiagnosabilityMBean#[com.oracle.jdbc:type=diagnosability,*]", "registerMBean";
permission javax.management.MBeanTrustPermission "register";

After this settings are in place, you will run into the Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1 issue. The root cause of that is, the the class loader of java agents (lotus.domino.AgentLoader) does not implement getResource(String name) and that leads to always returning null to the calling method. Since the orcale jdbc driver needs the glb files from the oracle.sql.converter_xcharset folder within the jar to work properly and they were loaded by the getRousource method mentioned above, this will not work! The result is the ArrayIndexOutOfBoundsException.

So the only solutions are either to use the driver from the file system (and use a jvm default class loader) or you change the class loading process as follows:

  1. create a custom class loader: public class CustomLoader extends ClassLoader {

    private final AgentLoader loader;
    
    public CustomLoader(AgentLoader agentLoader, ClassLoader parent) {
        super(parent);
        loader = agentLoader;
    }
    
    @Override
    public URL getResource(String name) {
    
        InputStream is = loader.getResourceAsStream(name);
        if (is == null) {
            return super.getResource(name);
        }
    
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    
        try {
            URL url = new URL("dominoinmemory", "", -1, name, new DominoInMemoryStreamHandler(name));
            System.out.println(url);
            return url;
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
    
    }
    
    private class DominoInMemoryStreamHandler extends URLStreamHandler {
    
        private String resName;
        byte[] content = null;
    
        public DominoInMemoryStreamHandler(String resName) {
            this.resName = resName;
        }
    
        @Override
        protected URLConnection openConnection(final URL u) throws IOException {
            if (!u.getProtocol().equals("dominoinmemory"))
                throw new IOException("Cannot handle protocol: " + u.getProtocol());
            InputStream is = loader.getResourceAsStream(resName);
            content = toByteArray(is);
    
            return new URLConnection(u) {
    
                @Override
                public int getContentLength() {
                    if (content != null) {
                        return content.length;
                    } else {
                        return super.getContentLength();
                    }
                }
    
                @Override
                public void connect() throws IOException {
                    if (content != null) {
                        connected = true;
                    } else {
                        throw new IOException("The resource '" + resName + "' was not found");
                    }
    
                }
    
                @Override
                public InputStream getInputStream() throws IOException {
                    return new ByteArrayInputStream(content);
                }
    
            };
        }
    
    }
    
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return output.toByteArray();
    }
    
  2. In the domino agent, before any other operation occurs, change the parent class loader of the AgentLoader with reflection

    public void NotesMain() {
    
        try {
            AgentLoader agentLoader = (AgentLoader) getClass().getClassLoader();
            Field f1 = agentLoader.getClass().getSuperclass().getDeclaredField("parent");
            f1.setAccessible(true);
            ClassLoader parent = (ClassLoader) f1.get(agentLoader);
            f1.set(agentLoader, new CustomLoader(agentLoader, parent));
            ...
    

Attention:

  • Use this at your own risk!
  • This code required two additional entries in the policy file:
    • permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
    • permission java.net.NetPermission "specifyStreamHandler";
0

精彩评论

暂无评论...
验证码 换一张
取 消