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:
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();
}
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";