Forum: Building VoltDB Applications

Post: VoltDB throws java.lang.IllegalAccessError

VoltDB throws java.lang.IllegalAccessError
webbean
Oct 12, 2013
We have a procedure that need an ObjectMapper instance to do its work like bellow:


import java.util.Map;
import org.voltdb.SQLStmt;
import org.voltdb.VoltProcedure;
import org.voltdb.VoltTable;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Demo extends VoltProcedure {

	private static ObjectMapper mapper = new ObjectMapper();

	private static final SQLStmt insert = new SQLStmt(
			"INSERT INTO DEMO VALUES(?);");

	public VoltTable[] run(String value) throws Exception {
		voltQueueSQL(insert, value);
		voltExecuteSQL();
		readValue(value);
		return null;
	}

	private void readValue(String value) throws Exception {
		Map<String, String> map = mapper.readValue(value,
				new TypeReference<Map<String, String>>() {
				});
		System.out.println(map);
	}
}


The procedure can be compiled successfully, but when it is called by the client application
the following exception is thrown:

java.lang.IllegalAccessError: tried to access class Demo$1 from class Demo
at Demo.print(Demo.java:26)
at Demo.run(Demo.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.voltdb.ProcedureRunner.call(ProcedureRunner.java:252)
at org.voltdb.iv2.ProcedureTask.processInitiateTask(ProcedureTask.java:103)
at org.voltdb.iv2.MpProcedureTask.run(MpProcedureTask.java:143)
at org.voltdb.iv2.Site.run(Site.java:487)
at java.lang.Thread.run(Thread.java:724)

If we remove the ObjectMapper instance, it works well.
Any idea about this?
bballard
Oct 12, 2013
To use a third-party library like jackson, the library needs to be loaded by the database process at runtime. If you only need the two classes TypeReference and ObjectMapper, then you could extract these from the jackson library jar file and put them in the same directory as the compiled stored procedure classes. Then in the DLL, you can use the IMPORT command to include them in the catalog. For example:

IMPORT CLASS org.mycompany.utils.*;

Reference: http://voltdb.com/docs/EnterpriseReleaseNotes/

If you need to include the entire jackson jar file, you could put it in the voltdb-<version>/lib/extension folder, but this will lock you in to having to use the command line interface to operate the database. The command-line scripts automatically include jars from the extension folder in the classpath. You wouldn't be able to use VoltDB Enterprise Manager, which doesn't currently allow additional library jar files to be deployed.
webbean
Oct 13, 2013
We want to include the entire jackson jar files, and did the following steps:

1. Copy these three jackson jars to voltdb/lib/extension folder.
jackson-annotations-2.2.3.jar
jackson-core-2.2.3.jar
jackson-databind-2.2.3.jar

2. Import the calsses our procedure requires in DDL file.
IMPORT CLASS com.fasterxml.jackson.core.type.TypeReference;
IMPORT CLASS com.fasterxml.jackson.databind.ObjectMapper;

3. Export the voltdb/lib/extension folder.
export CLASSPATH="./:/home/voltDB/voltdb-3.6/lib/extension/*"

4. Run voltdb uses the command line like this:
voltdb create catalog demo.jar host localhost zkport 2222

But the same error still occurred, it seems that these steps did not make the VoltDB work.

java.lang.IllegalAccessError: tried to access class Demo$1 from class Demo
at Demo.print(Demo.java:26)
at Demo.run(Demo.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.voltdb.ProcedureRunner.call(ProcedureRunner.ja va:252)
at org.voltdb.iv2.ProcedureTask.processInitiateTask(P rocedureTask.java:103)
at org.voltdb.iv2.MpProcedureTask.run(MpProcedureTask .java:143)
at org.voltdb.iv2.Site.run(Site.java:487)
at java.lang.Thread.run(Thread.java:724)

Is there any document about how load a third-party library into VoltDB?
bballard
Oct 13, 2013
If you added the 3 jar files to the extension folder, you do not need to use the IMPORT statement or set the CLASSPATH, so you could skip the steps you numbered 2 and 3.

It looks like the error is occurring in the Demo.print method when it is trying to access an anonymous inner class. I don't see any of that in the code, can you post an update?
webbean
Oct 13, 2013
Thanks, bballard. We have struggled with this for several days, please help.
In fact, there is no inner class in our source. The following is the entire list of our source.

The procedure class


package test;

import java.util.Map;

import org.voltdb.SQLStmt;
import org.voltdb.VoltProcedure;
import org.voltdb.VoltTable;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Demo extends VoltProcedure {

	private static ObjectMapper mapper = new ObjectMapper();

	private static final SQLStmt insert = new SQLStmt(
			"INSERT INTO DEMO VALUES(?);");

	public VoltTable[] run(String value) throws Exception {
		voltQueueSQL(insert, value);
		voltExecuteSQL();
		readValue(value);
		return null;
	}

	private void readValue(String value) throws Exception {
		Map<String, String> map = mapper.readValue(value,
				new TypeReference<Map<String, String>>() {
				});
		System.out.println(map);
	}
}


The client class

package test;

import org.voltdb.client.Client;
import org.voltdb.client.ClientFactory;

public class Test {

	public static void main(String[] args) throws Exception {
		Client client = ClientFactory.createClient();
		client.createConnection("localhost");
		client.callProcedure("Demo", "{"key","value"}");
		client.close();
	}
}


The DDL file
CREATE TABLE DEMO (
VALUE VARCHAR(100) NOT NULL,
PRIMARY KEY(VALUE)
);
CREATE PROCEDURE FROM CLASS test.Demo;

The libraries we added to the eclipse project
guava-11.0.2.jar
jackson-annotations-2.2.3.jar
jackson-core-2.2.3.jar
jackson-databind-2.2.3.jar
voltdb-3.6.jar

The libraries we added to lib/extension folder
guava-11.0.2.jar
jackson-annotations-2.2.3.jar
jackson-core-2.2.3.jar
jackson-databind-2.2.3.jar

The script we start VoltDB
export PATH="$PATH:/home/sun/Programs/voltDB/voltdb-3.6/bin"
export JAVA_HOME="/home/sun/Programs/java/jdk1.7.0_25"
voltdb compile --classpath="./:/home/sun/workspace/test/lib/*:/home/sun/workspace/test/bin/" -o test.jar /home/sun/workspace/test/test.ddl
export CLASSPATH="./:/home/sun/workspace/test/bin/"
voltdb create catalog test.jar host localhost zkport 2222

RUN
1. First, we start the VoltDB using the script above. Then
2. We run the Test class from the eclipse, and get the following error:

FATAL: Site: 0:2 encountered an unexpected error and will die, taking this VoltDB node down.
FATAL: Fatal exception
java.lang.IllegalAccessError: tried to access class test.Demo$1 from class test.Demo
at test.Demo.readValue(Demo.java:28)
at test.Demo.run(Demo.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.voltdb.ProcedureRunner.call(ProcedureRunner.java:253)
at org.voltdb.iv2.ProcedureTask.processInitiateTask(ProcedureTask.java:109)
at org.voltdb.iv2.MpProcedureTask.run(MpProcedureTask.java:143)
at org.voltdb.iv2.Site.run(Site.java:480)
at java.lang.Thread.run(Thread.java:724)
VoltDB has encountered an unrecoverable error and is exiting.

All files is contained in the attachment file.
bballard
Oct 13, 2013
The VoltDB compiler doesn't currently support anonymous inner classes. The anonymous inner class in your code is this:

new TypeReference<Map<String, String>>() {}

Note also the Demo$1.class file in the bin directory, that is the class file for the anonymous inner class.

There should be a way to use ObjectMapper without an anonymous inner class.
webbean
Oct 14, 2013
Thank you very much, bballard. We have resolved this problem by replacing the TypeReference to MapType.
mapper.readValue(value, mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class));