2

I have a beanshell code which use this librairies :

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;

import com.google.api.services.analytics.Analytics;
import com.google.api.services.analytics.AnalyticsScopes;
import com.google.api.services.analytics.Analytics.Data.Ga.Get;
import com.google.api.services.analytics.model.Accounts;
import com.google.api.services.analytics.model.GaData;
import com.google.api.services.analytics.model.GaData.DataTable;
import com.google.api.services.analytics.model.GaData.DataTable.Rows;
import com.google.api.services.analytics.model.GaData.DataTable.Rows.C;
import com.google.api.services.analytics.model.Profiles;
import com.google.api.services.analytics.model.Webproperties;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpRequest;

...

Line 28 : JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();

I download google-api-java-client jars, and add this at the beginning of my code :

addClassPath( "/Users/work/google-api-java-client/libs/" );

But it doesn't seem to work, I still have this error when running my code :

Evaluation Error: Sourced file: call_google_analytics.txt :
Typed variable declaration : Class: JsonFactory not found in namespace : 
at Line: 28 : in file: call_google_analytics.txt : JsonFactory 

What I'm doing wrong ?

user2178964
  • 124
  • 6
  • 16
  • 40

1 Answers1

1

I think it's a bug in Beanshell when it comes to mapping all jars in a directory on adding items to the Interpreter's classpath.

I have ran the following tests on the latest version you can depend on (without building the jar from sources). That would be org.apache-extras.beanshell:bsh:2.0b6 from 2016.

I have tried several options the manual provides on modifying the classpath, but none seem to work:

  • import *;
  • reloadClasses("");
  • reloadClasses("com.google.*");

You can verify that your folder is added fine, but just not mapped, by looking at the console (System.err) output from the following beanshell script:

addClassPath("/test-project/lib/");
reloadClasses("com.google.*");

Console output:

Start ClassPath Mapping
Mapping: Directory C:\~\test-project\lib
End ClassPath Mapping

This shows the added directory is known, but none of the jar files in the directory were mapped. Otherwise they would be printed in this output.

I have traced this down to following piece of code in the bsh.classpath.BshClassPath class:

    synchronized void map( URL url ) 
        throws IOException 
    { 
        String name = url.getFile();
        File f = new File( name );

        if ( f.isDirectory() ) {
            classMapping( "Directory "+ f.toString() );
            map( traverseDirForClasses( f ), new DirClassSource(f) );
        } else if ( isArchiveFileName( name ) ) {
            classMapping("Archive: "+url );
            map( searchJarForClasses( url ), new JarClassSource(url) );
        } 

We can see that on if ( f.isDirectory() it is only scanning for classes via traverseDirForClasses( f ). If you add a single Jar file then via else if ( isArchiveFileName( name ) it does scan the file for classes with searchJarForClasses( url ). Why it does not scan the directory for jar files and then classes is unknown to me.

As far as i'm concerned this is a bug, but i have not verified this with the latest 3.0 sources from github. If not a bug, then at least a missing feature. I have logged the following issue on github.

The workaround is to add each jar separately, see the following beanshell script + output:

libPath = "/test-project/lib/";
addClassPath(libPath+"google-api-client-1.33.0.jar");
addClassPath(libPath+"google-http-client-1.41.0.jar");
addClassPath(libPath+"google-http-client-apache-v2-1.41.0.jar");
addClassPath(libPath+"google-http-client-gson-1.41.0.jar");

import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;

JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
return "Factory: " + JSON_FACTORY;

Evaluated beanshell script output:

Factory: com.google.api.client.json.gson.GsonFactory@1a407d53

If you don't want to manually maintain a list of jar files, you could also perhaps scan the directory yourself for jar files, and call addClassPath for every jar found in the script itself.

For example with a beanshell script like this:

libPath = "C:\\...\\test-project\\lib";
dir = new File(libPath);
filter = new java.io.FilenameFilter() {
    public boolean accept(File dir, String name) {
        return name.toLowerCase().endsWith(".jar");
    }
};

files = dir.listFiles(filter);
for(file: files) {
    addClassPath(file.toURL());
}
slindenau
  • 1,091
  • 2
  • 11
  • 18