Problem building Java callout classes via Grunt versus directly on command line

This is our first Java callout-enabled API proxy. For our other APIs, we are using the Apigee grunt deployment tool: https://github.com/apigeecs/apigee-deploy-grunt-plugin/blob/master/Gruntfile.js

We are attempting to compile using it, so we've enabled the javac step in the file. This is the command:

javac java/src/com/<our-company>/ecommerce/<vendor-tool>/executor/*.java java/src/com/<our-company>/ecommerce/<vendor-tool>/util/*.java -d ./target/java/bin -cp /c/Git/<our-api>/java/lib/expressions-1.0.0.jar:/c/Git/<our-api>/java/lib/message-flow-1.0.0.jar -verbose

which works when running on the command line. However, when running it using Grunt, the classpath doesn't seem to get picked up and the necessary Apigee classes are coming back as not being found. Is this a discrepancy between how the Grunt shell processing works?

Here is the entry we have in Gruntfile.js:

javaCompile: {
	        	 command: 'javac java/src/com/<our-company>/ecommerce/<vendor-tool>/executor/*.java java/src/com/<our-company>/ecommerce/<vendor-tool>/util/*.java -d ./target/java/bin -cp /C/Git/<our-api>/java/lib/expressions-1.0.0.jar:/C/Git/<our-api>/java/lib/message-flow-1.0.0.jar -verbose'
	         }
1 11 754
11 REPLIES 11

@Diego Zuluaga, might you have a suggestion on this one?

Not applicable

Great question @Sean Case. I guess you're using Cygwin to run Grunt.js since I see some paths starting with jar:/C/. So, here a few suggestions for troubleshooting:

1). Check the drive. I noticed that the drive C your command is lowercase and in javaCompile is uppercase. Could this be the problem?

2) Use Powershell utility that comes with Github Desktop. I've found this utility better than Cygwin at times.

If you can build a quick example in github replicating the issue, I can take a look at it.

Thanks!

Thanks, @Diego Zuluaga, for this I'm actually using Git Bash.

1. Oddly enough, it doesn't seem to make any difference whether I'm using lower or upper-case. Ideally, I would like to be using relative paths as I did for the target directory and source files, but that was problematic while running javac on the command-line, as well.

2. I have the Github Powershell (Git cmd) and tried running it with the same result.

Is there a way to publish a sample to reproduce this issue? I wonder if this in "environment" issue (windows). We can find a single environment to try this. If you can create this sample in Cloud9, it should be quicker https://c9.io/.

I'm trying to create a sample on Cloud9 with the apigeesample Java cookbook example and the Grunt deployment tool. It's odd that I can run the exact same command within Git Bash directly and it will find the necessary packages within the jars and create the .class files, but when that javac command is run within Grunt, it isn't. I do think it might have something to do with a difference in environments when it comes to grunt.

I should note that I tried replacing grunt-shell with grunt-exec within the Grunt build with the same result: command worked fine on the command line, but wouldn't within the build tool

Not applicable

Hi @Sean Case and @Kristopher Kleva, thanks for the Cloud9 environment. I've just created javaCompileDZ under shell to try to run the command: javaCompileDZ : { command: 'javac java/src/com/apigeesample/*.java -cp ./lib/expressions-1.0.0.jar:./lib/message-flow-1.0.0.jar -d ./target -verbose' } The same command from Grunt and the terminal return the same error. Mostly about slf4j jar for being missing.

java/src/com/apigeesample/SubmitTaskAndPauseForCompletion.java:17: error: package org.slf4j does not exist import org.slf4j.LoggerFactory; My intent with running your example on Cloud9 is to make sure that Grunt and the command line in linux/unix work fine. However, you may still face issues with Windows as the syntax for javac is slightly different. In that case, I'd encourage you to check the command in Windows. For another customer, I recall we had to explicitly include each java file with the target flag.
command: 'javac -sourcepath ./java/src -d ./target/java/bin ' +
	            	'-cp java/lib/expressions-1.0.0.jar:java/lib/message-flow-1.0.0.jar:jar:java/lib/message-flow-1.0.1.jar:java/lib/javax-crypto.jar:java/lib/org-apache-commons-codec.jar ' + 
	            	'java/src/com/customer/apigee/utils/EncryptJavaCallout.java java/src/com/customer/midrange/utils/EncryptionUtils.java -source 1.6 -target 1.6'
Hope it helps!

I was able to create create the javac command which builds successfully within Windows cmd, but not when being run within Grunt. I wonder whether grunt-shell is defaulting to a shell other than cmd.exe? My hope is that we can get this working so that it will build and deploy consistently across platforms.

Awesome! Thanks @Sean Case for posting your solution. Agreed, ideally the same syntax works across all platforms in Grunt.js. I think I quickly tried grunt-run-java, and run into some issues with it. With more time, I'd love to try it again. If you guys figure some alternative, please post it in this thread.

I thought I saw your previous post with this function. However, I can't see it in the thread.

javaCompile: {
 command: function () {
      if (process.platform === 'win32' && !/^xterm/i.test(process.env.TERM)) {
              return 'javac -source 1.7 -target 1.7 java/src/com/<path to source>/*.java java/src/<path to source>/*.java -cp %cd%\java\lib\expressions-1.0.0.jar;%cd%\java\lib\message-flow-1.0.0.jar -d target/java/bin  -verbose';
     } else {
      	return 'javac java/src/com/<path to source>/*.java java/src/<path to source>/*.java -cp  /java/lib/expressions-1.0.0.jar: /java/lib/message-flow-1.0.0.jar -d ./target/java/bin  -verbose';
            }
           }
 }

Yes, I originally had that function. It picks out the command based on platform--which is good--but (per my post above) the win32 version only seems to work on the cmd.exe shell. I think it may be using Powershell instead of the standard command prompt.

I wonder if there's a way to print process.platform and tailor the command to that platform. Let me know if I can help with that.

Ok, I think I've found a solution. It appears that grunt-chalk (which is used by grunt-shell) is setting the terminal type based on platform. I was able to use it's line:

process.platform === 'win32' && !/^xterm/i.test(process.env.TERM)

along with a function within the grunt shell command to create a test and conditional build:

shell: {      
  options: {
     stderr: false,
     failOnError: true
  },
  javaCompile: {
     command: function () {
	        if (process.platform === 'win32') {
                  return 'javac -source 1.7 -target 1.7 java/src/com/<path to source>/*.java java/src/<path to source>/*.java -cp %cd%\java\lib\expressions-1.0.0.jar;%cd%\java\lib\message-flow-1.0.0.jar -d target/java/bin  -verbose';
	       } else {
	        	return 'javac -source 1.7 -target 1.7 java/src/com/<path to source>/*.java java/src/<path to source>/*.java -cp ${PWD}/java/lib/expressions-1.0.0.jar:${PWD}/java/lib/message-flow-1.0.0.jar -d ./target/java/bin  -verbose';
	              }
               }
     },
     javaJar : {
        command: 'jar cvf target/apiproxy/resources/java/BuildReviewURL.jar target/java/*'    }
 }

which seems to work.

I didn't have much time to investigate further, but was also hopeful that perhaps utilizing grunt-run-java (https://www.npmjs.com/package/grunt-run-java) may have been an opportunity to resolve this, too.

Thanks for the assistance!