If you haven't read the Tomcat 8 section of the Preparing Your Building Blocks For Learn SaaS and Newer Learn Versions document, you should. As Blackboard continues to update to newer technologies, we are often looking at it as an opportunity to improve the product from a security, performance, and code quality perspective. This upgrade was certainly no different. As a matter of fact, the changes involved in Tomcat 8 to the way Building Blocks are handled is pretty substantial. While not a huge effort on your part is required to take advantage, some refactoring is required.

 

One of the biggest changes is that all Java Server Pages (JSPs) in your Building Block are expected to be precompiled. Its not a terribly difficult thing to do, but it does require an additional step in your build process. Historically -- and even currently if I'm honest -- Tomcat has handled the compilation on-demand; that is to say, the first time a user accesses a JSP, all of the JSPs are compiled. Not a huge deal, particularly if there are only a few to go through, but there is always the chance of a runtime compilation error and even if not, a busy system with a large number of compilations can be quite inconvenient to a student trying to get some work done.

 

So, as I set out to update the Building Blocks documentation, I realized I had never actually compiled a JSP myself. As you are probably aware, I am a Gradle user, and so I assumed someone out there must have tackled this problem before. I was right, sort of, but not nearly as many as I'd anticipated. Because of that, and because using the Blackboard Tag Libraries without having the tld and jar file in the WEB-INF directory introduced a complexity I could not find a solution to, I thought I would share what I did so you can think about how you might approach this.

 

Enter Benjamin Muschko.

 

Benjamin has done quite a bit of work around Gradle plugins, some of which I have used before, so I wasn't too surprised to see he had built the gradle-tomcat-plugin. There are others out there, but since I was familiar with the author, I decided to use this one. Setting it up and installing it is pretty trivial and pretty well documented in the README for the project. Essentially you must create a build script at the top of the build.gradle file to set up the classpath with a link to the plugin so you can apply it in your build file. Here is what that looks like:

 

buildscript {
    repositories {
        jcenter()
    }


    dependencies {
        classpath 'com.bmuschko:gradle-tomcat-plugin:2.2.5'
    }
}


apply plugin: 'com.bmuschko.tomcat'
apply plugin: 'eclipse'
apply plugin: 'java'

 

Pretty straight forward, really. I should point out that I am using Gradle 3.3. If you are using an older version, upgrade. But if you don't want to, know that some of the syntax is different, and specifically, jcenter() is not a valid identifier, so you will need to manually reference as a maven repository, similar to how you declare the Blackboard maven repository. There are a millions google results to help you achieve that goal.

 

So I did this and the plugin was installed, and everything was great. The next step is to configure the plugin to use the appropriate version of Tomcat. This is extra important, because this plugin actually allows you to spin up a Tomcat server and run your code directly from Gradle. I'm not using it, but you definitely can. This also sets up your environment for compiling your JSPs.

 

Here is what that looks like according to the README :

 

// define the project's dependencies
dependencies {
   def tomcatVersion = '8.0.42'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
           "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
           "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
  
    providedCompile "javax.servlet:servlet-api:2.5"


  // Dependencies are libraries needed to build, but should not be included in the B2 WAR.
  // You should NEVER include Learn JARs (other than webapis) in your B2.
  providedCompile( "blackboard.platform:bb-platform:$project.ext.learnVersion" ) { transitive = false }
  providedCompile( "blackboard.platform:bb-taglibs:$project.ext.learnVersion" ) { transitive = false }

 

So this was great. I did some optional configuration to turn of TLD verification and blam. Off to the races. Well, so I thought...

 

I ran the gradle command to compile the JSPs: gradle tomcatJasper, and after a few seconds, I received an error:

$ gradle tJ

:compileJava UP-TO-DATE
:processResources
:classes
:tomcatJasper FAILED


FAILURE: Build failed with an exception.


* What went wrong:
Execution failed for task ':tomcatJasper'.
> org.apache.jasper.JasperException: Unable to find taglib "bbNG" for URI: /bbNG

 

I did some noodling and playing and testing, and I figured that the problem must be that the classpath of the project wasn't being used as the classpath for the JSP compilation. I googled and yahooed and binged and I couldn't find any clear answer, so I did what I always do in this situation. I cried. Well, ok, I didn't, but I wanted to! Instead, I got a fresh cup of coffee and started reading the plugin code. In TomcatRun task, I came across a comment that said: "The web application's classpath. This classpath usually contains the external libraries assigned to the compile and runtime configurations."

 

I then looked at what the code was doing to set up the directory that the compilation was taking place, and it was essentially copying the WEB-INF folder. Because we use provideCompile to add the taglibs to the classpath but explicitly not include the jar file in the WEB-INF directory, there was no TLD to be found.

 

To test, I changed the dependency to compile and re-ran the test. I got a different message this time. AH HA!!

 

Of course, I can't leave the taglib in the Building Block, and I'm too lazy to change it back and forth to compile the JSP and then the WAR file, I had essentially confirmed the cause, but was still no closer to figuring out the solution. Again, I set my fingers in web crawl mode and hunkered down to search for ideas. After a couple of hours, my head hurt from banging it on the desk and my fingers were tired, and I still had no solution.

 

I took a step back and just looked through the build.gradle file to see what was there. I had tried adding additionalRuntimeResources, but that only works for TomcatRun and TomcatRunWar. I had tried creating my own classpath and adding the taglibs to the Gradle class path, and nothing. Then I noticed the tomcat dependency, and specifically how it was adding in a few libraries specifically to configure the plugin to setup the correct Tomcat version. I thought, what the heck, lets add the taglib there. I ran the task, and wouldn't you know it! I got the same error I got when I used compile instead of providedCompile. The error referenced the fact that it could not find one of the Blackboard objects, so i said, what the heck, let's add the platform jar, too, and low and behold, I got an error about the JSTL fmt tag library. I added that one, too, and I got those two words every Gradle user loves: BUILD SUCCESSFUL.

 

Here's what that looked like:

 

// define the project's dependencies
dependencies {
   def tomcatVersion = '8.0.42'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
           "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
           "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}",
           "blackboard.platform:bb-taglibs:$project.ext.learnVersion",
           "blackboard.platform:bb-platform:$project.ext.learnVersion",
           "javax.servlet:jstl:1.2"


  providedCompile "javax.servlet:servlet-api:2.5"


  // Dependencies are libraries needed to build, but should not be included in the B2 WAR.
  // You should NEVER include Learn JARs (other than webapis) in your B2.
  provompile( "blackboard.platform:bb-platform:$project.ext.learnVersion" ) { transitive = false }
  providedCompile( "blackboard.platform:bb-taglibs:$project.ext.learnVersion" ) { transitive = false }

 

So after all of that, I finally had Gradle compiling my JSPs. Since I already survived the struggle, I thought I would share here with you!

 

I am very overdue in updating the basic-b2-template project, so I am doing that now, to include the precompilation and any other updates that are missing since Gradle 1.6 was cool. I will update the group when it is complete.

 

happy Developing!!

-scott