Null pointer exception using Sikuli in Java

Asked by Borja Miñano

Hi,

I am facing a null pointer exception when trying to use sikuli api in my project:

java.lang.ExceptionInInitializerError
 at eu.iac3.mathMS.guiTestingTest.GuiTestingTest.testEditor(GuiTestingTest.java:144)
Caused by: java.lang.NullPointerException
 at org.sikuli.script.RunTime.init(RunTime.java:635)
 at org.sikuli.script.RunTime.get(RunTime.java:291)
 at org.sikuli.script.RunTime.get(RunTime.java:126)
 at org.sikuli.script.RunTime.get(RunTime.java:316)
 at org.sikuli.script.Region.<clinit>(Region.java:31)

I am using maven integration in eclipse to launch an OSGI Springframework test and I think I have a misconfiguration or something similar.
I set the SIKULI_HOME to the sikuli installation path.

The error seems relative to getting the parent directory of a class. The following line returns null and is the origin of the problem:
File fSxBase = fSxBaseJar.getParentFile();
The class related to the problem is org.sikuli.ide.SikuliIDE or org.sikuli.setup.RunSetup, I am not sure.

Can anyone help me? I am desperate, using Sikuli api would help a lot in my project tests.

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
Borja Miñano
Solved:
Last query:
Last reply:
Revision history for this message
angelo (angelo-j) said :
#1

Can you post a small example code and pom.xml?

Revision history for this message
Borja Miñano (bminyano) said :
#2

There is the code I try to execute. It is a junit test simple, as I was starting to integrate Sikuli as a conceptual test.

    public void testEditor() {
        try {
            Debug.setDebugLevel(3);
            Screen s = new Screen();
        }
        catch (Exception e) {
            e.printStackTrace(System.out);
            fail("Error: " + e.getMessage());
        }
    }

My pom.xml is quite long, I'll copy an extract with relevant information:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
...

 <dependencies>
 ...
  <dependency>
   <groupId>com.sikulix</groupId>
   <artifactId>sikulixapi</artifactId>
   <version>1.1.0</version>
  </dependency>
  ...
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>1.4.0</version>
    <extensions>true</extensions>
    <configuration>
     <instructions>
      <Bundle-Version>${pom.version}</Bundle-Version>
      <Bundle-Name>${pom.artifactId}</Bundle-Name>
      <Bundle-Vendor>${pom.organization.name}</Bundle-Vendor>
      <Bundle-Description>${pom.description}</Bundle-Description>
      <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
      <Export-Package>${pom.groupId}.${pom.artifactId}</Export-Package>
      <Import-Package>org.osgi.service.log,javax.xml.namespace, javax.xml.xpath,org.apache.xindice.client.xmldb,javax.swing, javax.swing.border, javax.swing.tree, org.json,
       org.apache.xml.serialize, org.apache.commons.lang3, net.sf.saxon, net.sf.saxon.xpath, javax.xml.transform,javax.xml.parsers,javax.xml.validation,javax.xml.transform.dom,javax.xml.transform.stream,org.w3c.dom,org.xml.sax,org.osgi.framework;version="1.4.0"</Import-Package>
      <Bundle-Activator>${pom.groupId}.${pom.artifactId}.Activator</Bundle-Activator>
      <Bundle-ClassPath>., lib/gson-2.3.1.jar,lib/xsom-20081112.jar, lib/relaxngDatatype-2.2.jar, lib/xml-apis.jar, lib/sikulixapi-1.1.0.jar, lib/sikulix.jar, lib/sikulixsetup-1.1.0.jar</Bundle-ClassPath>
      <Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
     </instructions>
    </configuration>
   </plugin>

   ...
   <plugin>
    <artifactId>maven-clean-plugin</artifactId>
    <version>2.2</version>
    <executions>
     <execution>
      <id>auto-clean</id>
      <phase>initialize</phase>
      <goals>
       <goal>clean</goal>
      </goals>
     </execution>
    </executions>
   </plugin>
   ...
  </plugins>
 </build>
</project>

I am using sikuli jars internally in the bundle, so I declare them in the bundle-classpath to make them visible to the class loader.
They are visible, since Class.forName("org.sikuli.ide.SikuliIDE"); and Class.forName("org.sikuli.setup.RunSetup"); return the classes.
However, it cannot get the parent file, maybe because they are inside a bundle.

Revision history for this message
angelo (angelo-j) said :
#3

Hi Borja,

this is a bit tricky to reproduce. Can u make a small project with a small pom.xml that contains all necessary information? For example I cannot see which test suite you are using...

Revision history for this message
Borja Miñano (bminyano) said :
#4

Hi Angelo,

Yes, this is a twisted configuration.
I made a small eclipse-maven project with the minimum (no code, only sikuli testing environment), the link is the following: https://drive.google.com/file/d/0B8iGIDREhD9wS3c3UV9QNTFwelE/view?usp=sharing

Our testsuite is an embedded junit that works inside the osgi environment from springframework. It is a bit old now, but we are not planning to move for now.

If you need additional information, please ask.
Thank you for the interest.

Revision history for this message
RaiMan (raimund-hocke) said :
#5

I have no idea, why this happens in your special setup.

the code in org.sikuli.script.RunTime

    CodeSource codeSrc = clsRef.getProtectionDomain().getCodeSource();
    String base = null;
    if (codeSrc != null && codeSrc.getLocation() != null) {
      base = FileManager.slashify(codeSrc.getLocation().getPath(), false);
    }
    appType = "from a jar";
    if (base != null) {
      fSxBaseJar = new File(base);
      String jn = fSxBaseJar.getName();
      fSxBase = fSxBaseJar.getParentFile();
      log(lvl, "runs as %s in: %s", jn, fSxBase.getAbsolutePath());
      if (jn.contains("classes")) {

where the last line is the crash point (line 635)

it only gets there, if base is != null and I have no idea, why jn can be null at this place.

The only idea:
base at this point might be be an empty string, but File.getName() according to the API, does not return null.

Did you have the log message, that is produced before the crashing line:
.... runs as .... in: ....

if not, then try to add
-Dsikuli.Debug=3
to the Java environment

I am sorry, but I have no means, to test the behavior in such environment, that you are using.

Revision history for this message
Borja Miñano (bminyano) said :
#6

Hi RaiMan,

Sure, the log messages before the exception are the following:
[debug] Debug.init: from sikuli.Debug: on: 3
[debug] RunTimeINIT: lsb_release -i -r -s
[debug] RunTimeINIT: loadOptions: check: /home/bminano/workspace/guiTestingSmall
[debug] RunTimeINIT: loadOptions: check: /home/bminano
[debug] RunTimeINIT: loadOptions: check: /home/bminano/.Sikulix/SikulixStore
[debug] RunTimeINIT: global init: entering as: API
[debug] RunTimeINIT: ScreenDevice 0 has (0,0) --- will be primary Screen(0)
[debug] RunTimeINIT: Monitor 0: (0, 0) 1680 x 1050

I also checked the source code and tried to figure out what can be happening. I adapted some of the source code in my test:
        Class clsRef = null;
        try {
            clsRef = Class.forName("org.sikuli.ide.SikuliIDE");
        }
        catch (Exception ex) {
            System.out.println("Doesn't load SikuliIDE");
        }
        try {
            clsRef = Class.forName("org.sikuli.setup.RunSetup");
        }
        catch (Exception ex) {
            System.out.println("Doesn't load RunSetup");
        }
        CodeSource codeSrc = clsRef.getProtectionDomain().getCodeSource();
        String base = null;
        if (codeSrc != null && codeSrc.getLocation() != null) {
            base = FileManager.slashify(codeSrc.getLocation().getPath(), false);
        }
        System.out.println(base);
        if (base != null) {
            File fSxBaseJar = new File(base);
            String jn = fSxBaseJar.getName();
            File fSxBase = fSxBaseJar.getParentFile();
            System.out.println(jn);
            System.out.println(fSxBase);
            System.out.println(fSxBase.getAbsolutePath());
        }
The exception is launched in the last print since fSxBase is null. The problem I see here is that the fSxBaseJar file is [onTheFly-test-bundle]GuiTestingTest[1957655256], so maybe it is not possible to get its parent file.

Revision history for this message
RaiMan (raimund-hocke) said :
#7

Thanks for testing, but ...

--1. the log message:
.... runs as .... in: .... (produced by log(lvl, "runs as %s in: %s", jn, fSxBase.getAbsolutePath()); )
is missing in the log you gave me.

--2. in your test with a correct environment setup (only sikulixapi.jar is on class path), clsRef should be null, since neither clsRef = Class.forName("org.sikuli.ide.SikuliIDE"); nor clsRef = Class.forName("org.sikuli.setup.RunSetup"); should work.

I see 2 aspects:
- purge SIKULI_HOME from your environment (SikuliX 1.1.0+ does not need nor use that any more). So if you accidentally have other references to "wrong" Sikuli stuff, then you should get other errors.
- take care, that neither sikulix.jar nor the sikulixsetup...jar is on class path at runtime.

Take care that you are using the final 1.1.0, otherwise I cannot help you.

You are saying, you are running a Maven project: Is sikulixapi.jar referenced so that it is taken from MavenCentral?

Revision history for this message
Borja Miñano (bminyano) said :
#8

--1.
It is missing because it is the line that throws the exception. Line 635 is the following one in the version I am using (1.1.0):
log(lvl, "runs as %s in: %s", jn, fSxBase.getAbsolutePath());
I assumed fSxBase is null so it throws the exception

--2. I have read again the source code and I found I was confused about having to load SikuliIDE or RunSetup classes. I thought it was necessary so I added it to the classpath. I removed it now from my configuration.

I have removed the SIKULI_HOME variable as suggested.
I also removed all references to the two jars in order not to be loaded in the classpath and checked that they are not.

This is my dependency in pom.xml:
  <dependency>
   <groupId>com.sikulix</groupId>
   <artifactId>sikulixapi</artifactId>
   <version>1.1.0</version>
  </dependency>
So I think this is the final 1.1.0, am I wrong? May I try the 1.1.1 nightly build?

The error still persist the same, by the way.

Revision history for this message
RaiMan (raimund-hocke) said :
#9

Your dependency pulls the final 1.1.0

1.1.1 does not make a difference about that problem.

Repeat your test from comment #6 now using
Class clsRef = Class.forName("org.sikuli.script.RunTime");

leaving out the 2 try/catch blocks

Revision history for this message
Borja Miñano (bminyano) said :
#10

Tried your suggestion.
Confirmed then that fSxBaseJar.getParentFile() returns null.

I have the intuition that this is related to the (dis)ability of OSGI to handle with Files the way is made in Sikuli.
What does this code work for? Maybe I could think a workaround.
I am going to osgify the jar to moving it out form the test bundle into its own one.

Revision history for this message
RaiMan (raimund-hocke) said :
#11

This is mainly for getting the resource location for exporting resources in the different runtime situations (from jar, from Maven or standard project context in Java IDE's, ....).

Ok, I understand, that I should add the awareness of OSGI in version 2.
But currently I am without any knowledge about this area.

Revision history for this message
Borja Miñano (bminyano) said :
#12

I finally make it work!!!!

The problem came from having sikulixapi.jar inside the bundle classpath as an internal library. For some reason, the File locator cannot get the correct parent path in this situation.

So what finally worked was to osgify the sikulixapi.jar, make it independent from the test bundle and load the dependencies through OSGI system.

For your interest, this is the MANIFEST.MF that makes the jar OSGI compliant:
Manifest-Version: 1.0
Built-By: jenkins
Build-Jdk: 1.7.0_55
Created-By: Apache Maven 3.2.1
Archiver-Version: Plexus Archiver
Export-Package: org.sikuli.basics,org.sikuli.guide,org.sikuli.natives,org.sikuli.remoteinterfaces.common,org.sikuli.remoteinterfaces.entities,org.sikuli.script,org.sikuli.script.compare,org.sikuli.util
Bundle-Vendor: SpringSource
Ant-Version: Apache Ant 1.5.4
Bundle-Version: 1.1.0
Bundle-Name: Sikulix
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.sikuli.script.Sikulix
Import-Package: javax.swing,javax.imageio

The Import-Package is not complete, as I imagine, there are other dependent libraries to be configured there, but for my initial setup it was enough.

RaiMan, if you are interested in provide the jar as OSGI compliant I can help you. I am not an expert, but have some experience.

You can use the following Maven plugin to create the MANIFEST:
   <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>1.4.0</version>
    <extensions>true</extensions>
    <configuration>
     <instructions>
      <Bundle-Version>${pom.version}</Bundle-Version>
      <Bundle-Name>${pom.artifactId}</Bundle-Name>
      <Bundle-Vendor>${pom.organization.name}</Bundle-Vendor>
      <Bundle-Description>${pom.description}</Bundle-Description>
      <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
      <Export-Package>${pom.groupId}.${pom.artifactId}</Export-Package>
      <Import-Package>javax.swing, javax.imageio, ...</Import-Package>
     </instructions>
    </configuration>
   </plugin>

I hope it helps.

Revision history for this message
RaiMan (raimund-hocke) said :
#13

Thanks for the input. I will try to understand and somehow add it or at least the information.
Very appreciated.