Liferay 7 + JRebel

General Blogs August 18, 2017 By Andrew Jardine

I recently responded to the thread in the forums where the poster was asking about Liferay 7 and it's compatibility with OSGI. I am a huge JRebel fan myself and, based on what I know of JRebel and how it works, couldn't think of a reason why it wouldn't work -- but, I hadn't yet tried it out. I started doing some research as well as having a chat with the infamous David Nebinger about JRebel, OSGI, whether or not it should in theory work etc. Liferay 7's achitecture is -- let's say -- is "a bit of a shift" from the past versions, so I wasn't sure. So my question was, with the new model and all this OSGI stuff, "Does this mean I can't or don't need JRebel anymore?". The short answer is no, youl use JRebel, and, for me, I think there is still heaps of value in the tool.

In this post I'm going to share what I found and what I did in hopes that it will shortcut this task for others. First, for any JRebel newcomers, I'm going to do a quick "What is JRebel". If you already know about JRebel, you can skip that section. Next we'll setup our agent configuration and logging levels stuff so that you can use to reveal some additional logging and help you figure out whether or not things are working. Last we'll wrap it up by showing how to configure/generate the rebel.xml file -- specifically how to manage this for the new Gradle project structure.

 

What the heck is a JRebel?


I remember working with a guy who would spend hours writing thousands of lines of code before he compiled it. One part of his reasoning for this was because he hated waiting for deployments. The other part was that he like to think of it as a sort of game of compiler roulette, taking his chances and hoping that everything worked. Me? I tend to be the bits and pieces type developer -- I know what I want to achieve but I take it one bite at a time. My approach means A LOT of updated deploys -- but this, for those of you who are not familiar, is where JRebel comes into the game. It might not be for everyone, but if you are a bits and pieces person like myself, this might be for you.

I started using JRebel back when I was working with Liferay 6.1 -- something that feels like an eternity ago now. For all of my 6.1 and 6.2 work, JRebel was one of me besties, saving me countless hours a day. JRebel is a product written by Zeroturnaround -- who incidentally has some of the best customer service I have ever received in my entire life. With JRebel, you can dynamically reload classes and resources at runtime which means that I can make a change in my IDE, compile it (just the class, not the entire plugin/project) and BAM! latest version of the class is live in the runtime -- no deployment necessary. "C'mon" you might say, "...deployments only take like 7 seconds to do". True, but when you do thousands of them over the course of a large engagement, those 7 seconds add up. JRebel manages to do this hot swap of the class by using a configuration file that tells it which "disk location" to monitor. The JRebel Agent does the rest -- swapping the old class for the new one when the change is detected. If you work the way I do, this is an incredible tool that allows me to build a portlet using a fraction of the number of deploys I otherwise would have. It also keeps track of deploys versus reloads and tries to give you some metrics on how much time you have saved by figuring out how many deploys you DIDN'T have to make. The licensing cost works out to be something like $1.50 a day -- I don't know how much your time is worth? but it's definitely worth it for me. I also justify the cost as considering that it makes me a more efficient developer, and since I am usually paid by the hour, it allows me to provide some added value (in terms of savings) for my clients.

So that's my pitch on JRebel. Now, let's have a look at how we can use it with Liferay 7. If you have used it before, it's not earth shattering details so don't get too excited.

 

Agent Configuration


Before we blast into this I want to take a couple of lines to just explain where my hesitation came from. All my JRebel + OSGI research referenced architectures where you as the user start the Felix container directly. Knowing that, in Liferay 7, you start Tomcat and then Liferay handles starting the OSGI bit for you at startup -- I wasn't sure how all the agent configuration settings would work. In the end though, there is no real difference because the agent is attached to the JVM process. Tomcat is what we start but the OSGI container end up running under the same process, so all the settings are still applicable. Great so let's get to it.

My preference is to manage my server outside the IDE process. I start it from the command line under it's own process. Likewise, with JRebel, my preference is to use the standalone agent configured to start with the server starts rather than trying to use the IDE plugins. As a result, I'm going to outline "My Way" -- you may need to make alterations if you don't use the same approach I do.

Start by going to your $TOMCAT_HOME/bin folder and open up the setenv.sh (or .bat for windows folks out there). In this file you want to add the following line -- updating the paths to match your local environment of course.

	# JREBEL agent configuration
	JAVA_OPTS="$JAVA_OPTS -javaagent:/home/aj/jrebel/jrebel.jar -Drebel.log.file=/home/aj/projects/liferay7/liferay-portal/tomcat/log/rebel{time}.log -Drebel.log=true -Xbootclasspath/p:/home/aj/projects/liferay7/liferay-portal/tomcat/temp/rebelboot.jar"

That's all you need -- provided you have installed JRebel already that is. There are a couple of things to note here though. First, I set the log location to be the same place Tomcat logs so that log cleanup that occurs every now and then can all be done in the one place. Second, the log is set to TRUE so which actually causes debug level logging to occur. There is a marginal (on my machine anyway) performance hit that comes with this, but using debug means that I see messages in the log confirming that locations are being monitored and that files are being reloaded. I like to work this way because it takes the "mystery" out of it for me. Sometimes a class won't get reloaded. If I have a statement output for everytime that it does then I know what the problem might be if my latest changes aren't reflected. Since I always tail my log while I am developing, this is a good approach for me. The last thing to mention here is the last parameter. If it is your first time running the agent, the startup with not complete because that process will generate that file. So for first timers, you need to do a double start.

With these settings in place, go ahead and fire up your server. Watch the log and validate that you see the JRebel output near the opening statements. If you don't see them, then you have something wrong in your configuration. Once your server has started up and you are sure the agent is running, move on to the last section.

 

JRebel Configuration


NOTE: There is an update below the conclusion that demonstrates how to you have the agent configuration added to all your modules auto-magically instead of having to do each one manually.
 
WARNING! When I started down this path I had a lot of problems and could only get PART of the JRebel process working. I was able to see the classes being reloaded but the actual class references weren't being updated until I restarted. In the end I managed to fix this by updating my version to the latest JRebel agent (as of this post) 7.0.13. Make sure you have this minimum version before you go on.

At first to get this thing going I just created my rebel.xml file manually. But then it occurred to me that there was probably a gradle plugin that I could use. Of course, there is and you can find all the details on how to use it (there are a lot of options) here: https://manuals.zeroturnaround.com/jrebel/standalone/gradle.html. Before you can use it though, you'll need to make sure that you have a repository where Gradle can find it. Open the settings.gradle for your Liferay Workspace and update the repositories block to include this maven repository --

	maven {
		url "https://plugins.gradle.org/m2/"
	}

If you want to just use the basic out of the box to get going, all you have to do is open your build.gradle file for your module and add the following to the TOP of the file. The location in the file is important because otherwise you'll anger the gradle gods. My sample project looked like this in the end --

	plugins {
	    id "org.zeroturnaround.gradle.jrebel" version "1.1.7"
	}

	jar.dependsOn(generateRebel)

	dependencies {
	    compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "2.0.0"
	    compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "2.0.0"
	    compileOnly group: "javax.portlet", name: "portlet-api", version: "2.0"
	    compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"
	    compileOnly group: "jstl", name: "jstl", version: "1.2"
	    compileOnly group: "org.osgi", name: "osgi.cmpn", version: "6.0.0"
	}

If you are using an IDE you should now see a task for "generateRebel" -- or alternatively you can go to your command line and use the gradle wrapper with the task name. Depending on how nested your project is it will look something like this --

	$>../../../gradlew generateRebel

If everything is configured correctly, you will see the usual BUILD SUCCESSFUL. But what happened? where is my rebel.xml? The gradle plugin works the same way the Maven one does, if you are familiar with it. It doesn't put the rebel.xml file into the main source tree, if just puts it into the compiled area. So if you go into your modules build/resource/main directory, you'll see it there. Open the file up and you should see something like this --

	<?xml version="1.0" encoding="UTF-8"?>

	<!--
	  This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
	  Refer to https://manuals.zeroturnaround.com/jrebel/standalone/config.html for more information.
	-->
	<application generated-by="gradle" build-tool-version="3.0" plugin-version="1.1.7" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_2.xsd">
	  <classpath>
	    <dir name="/home/aj/projects/liferay7/liferay-workspace/modules/apps/sample-web/build/resources/main">
	    </dir>
	    <dir name="/home/aj/projects/liferay7/liferay-workspace/modules/apps/sample-web/build/classes/main">
	    </dir>
	  </classpath>

	</application>

This is what we were talking about earlier, disk locations that we want the agent to monitor for changes so that we can hot swap them in without a deployment. Now that you have your rebel.xml file generated, go ahead and deploy your plugin. Based on the agent configuration above, you should see something similar to this in the log.

	13:45:22,369 INFO  [fileinstall-/home/aj/projects/liferay7/liferay-portal/osgi/modules][BundleStartStopLogger:38] STOPPED com.sample.web_1.0.0 [541]
	2017-08-18 13:45:22 JRebel: Directory '/home/aj/projects/liferay7/liferay-workspace/modules/apps/sample-web/build/resources/main' will be monitored for changes.
	2017-08-18 13:45:22 JRebel: Directory '/home/aj/projects/liferay7/liferay-workspace/modules/apps/sample-web/build/classes/main' will be monitored for changes.
	13:45:23,123 INFO  [Refresh Thread: Equinox Container: 1029feb8-1a84-0017-1c1b-d63bd3f6b422][BundleStartStopLogger:35] STARTED com.sample.web_1.0.0 [541]

.. and just like that you are good to go with this plugin module, making changes and not having to deploy it. You'll also notice (if you didn't already in the rebel.xml) that there is a reference to the resources folder as well which, yes, means you should be able to update properties files, spring configs etc. But let's test it out.

My test is simple. I have, as part of the web module a class called SamplePortlet which extends the MVCPortlet. in the render method for this portlet I added a line of code -- something obvious, I used a logging statement.

public class SamplePortlet extends MVCPortlet
{
    private static final Log _log = LogFactoryUtil.getLog(SamplePortlet.class);

		@Override
    public void render(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException
    {
        _log.info("JRebel reloaded my class!");

        super.render(renderRequest, renderResponse);
    }
}

Save the file and make sure to compile it to be sure that a new .class file is generated in the build folder. I use intellij so for me I have the recompile hot key combination as CTRL-SHIFT-S. Once the class is recompiled, I see this show up in the log.

2017-08-18 13:46:09 JRebel: Reloading class 'com.sample.portlet.SamplePortlet'.

... reloading the page with my portlet shows me the new log message I just added -- no deployment necessary.

 

Conclusion


I guess the original question -- Can I use JRebel with Liferay 7 can be answered as "yes". Now in all fairness this is a pretty rudimentary example. I have also tried it (successfully) with a more complicated scenario where I have a shared service that is used in several modules and have seen the update to the service reflected across all the modules accordingly. While two tests can't and probably shouldn't be considered enough to wipe your hands, it's certainly a promising start.

On the whole, if you are a JRebel user and want to continue to use it with Liferay 7 to help minimize your deploys and accelerate your dev cycles, looks like you're good to go.

 

Update

Adding that block to each of your modules is a bit of a pain in the butt. If you want to have the code applied to all your modules without having to edit them individually, then you can use this little trick (and grow a little love for Gradle at the same time).

In your main Liferay workspace, beside the settings.properties there is a file called build.gradle. Chances are this file is empty. We can use this file to add the following code.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.zeroturnaround:gradle-jrebel-plugin:1.1.7"
    }
}
allprojects { project ->
    plugins.withId('java') {
        project.apply plugin: 'org.zeroturnaround.gradle.jrebel'
        def jarTask = project.tasks.findByName('jar')
        if (jarTask) {
            jarTask.dependsOn(generateRebel)
        }
        def warTask = project.tasks.findByName('war')
        if (warTask) {
            warTask.dependsOn(generateRebel)
        }
    }
}

 

The one thing that I like about gradle is that it reads nicely. All we're doing here is saying "for any project we have, make sure that the generateRebel task is available, as long as the project type is either a war or a jar." Once you have this in place you can go ahead and remove the other snippet from your individual module build.gradle files. That's it -- now you don't have to try to remember to add the snippet to each module!

Multiple service.xml Files in a Single Plugin

Technical Blogs July 17, 2017 By Andrew Jardine

I think before we get rolling here I need to first admit that blogging about 6.2 features and functions is maybe not the best practice since we should all be planning our move to 7 in preparation for the end of days that are coming. In truth, I have wanted to write this post since before Liferay 7/DXP hit the stage but never had the time. With that said, I think that there are still A LOT of people both today and in the future who will remain on 6.2 and may benefit from some of this information. So -- this post is for you :). I should also add that this might be something that you can do in 7 as well, though I think the new architecture probably makes this feature a little less sexy. 

I'm going to say something now that some of you will boo. Ready? I'm a fan of Service Builder. It's not the perfect solution, but if you have spent the time to study the hard benefits, and then also consider the soft benefits, then I'm sure you will agree that it is not the most evil thing ever produced. It's not perfect, but nothing is and most of the time the hardship it brings is the result of misuse. As much as I love service builder though there is one thing that always bothered me about it -- In particular, the fact that I can only have ONE package configuration for all my entities. In this post I want to look at this aparent limitation, and then show an (undocumented? to my knowledge at least) build parameter that can be used to remove the limitation.

 

Setting the Stage


Ok -- let's start with the black and white. You need to write 5 portlets. Some people will put them all in one plugin (5 portlet descriptors in the one plugin package), others will make one plugin project per portlet. I've witnessed and participated in many a debates over which solution is better. I'm not writing this to debate what approach is best. There are lots of compelling arguments for both patterns from modularity, to data sharing, to resource usage, and more. Personally, I think a hybrid of the two is what's (often) best. Let's say 3 of my 5 portlets are related enough that it makes sense to keep them together, and that the other 2 warrant their own projects. In the case of the individual plugins? one service.xml file with your entity deinitions, all under one package, makes sense. For the case where you have 3 portlets in one plugin project, all needing service builder entities -- you're still using one package to store everything. But I don't want that. I want a pacakge for each of my entities.

Let's be honest here, my beef with this is pedantic. If I have three entities --

  1. Course
  2. Student
  3. Professor

... I don't necessarily want all the generated code to be mashed under one package. I want to have a base package of com.university and then have a package for each so that I end up with --

  1. com.university.course
  2. com.university.student
  3. com.university.professor

.. just like Liferay does in the core. Like Liferay does. That is what lead me to dig in and find out how I might be able to do this, because I know that Liferay uses service builder to generate it's entities and they're not all stuck under a single package. At the time of this investigation I was using maven. I went wading through the liferay-maven-support source. Low and behold. there is a parameter that you can pass to service builder to tell it where to find the service.xml file -- meaning I don't have to keep one file under /WEB-INF and in fact there is no reason why I can't have several service.xml files (as Liferay does) and pass the file as a parameter. Multiple service.xml files means multiple package declarations, but all within the same project. Note though, that you do have to patch the maven tools to get this to work because there is a bug that (to my knowledge) is not yet fixed -- you can reference it here: https://issues.liferay.com/browse/MAVEN-147?jql=project%20%3D%20MAVEN%20AND%20text%20~%20%22service%20build%20and%22

There is no point is trying to use the feature before you patch it, so let's run through this logically. First we'll fix the maven stuff. Then we'll make a sample project to show how to use it.

 

Patching Maven


We're going to start get getting the maven tools locally. You can clone the repo if you like, but I'm going to just download the source from Github. Your version will of course matter. This link is for 6.2.5 GA6 -- but you can use the tags to pick the version you need: https://github.com/liferay/liferay-maven-support/tree/6.2.5

Again, instead of cloning, I'm going to just download a zip as you can see from the image below.

Once everything is downloaded, unpack the archive to a location. In my case, I have an area of my disk where I keep things that are common across my (Liferay) projects. I use /opt/liferay so I'm going to upack it there so that I end up with /opt/liferay/liferay-maven-support-6.2.5 -- and from here on in, I'll refer to his location as LIFERAY_MAVEN_SUPPORT.

The file we need to edit is a java file that is in this package. Open the file ServiceBuilderMojo.java that is found in LIFERAY_MAVEN_SUPPORT/plugins/liferay-maven-plugin/src/main/java/com/liferay/maven/plugins/. If you are not deeply familiar with Maven, and building your own plugins, using classes that are post fixed with the word *Mojo is the convention. If you take a moment to look through the files in this directory you'll probably find at least a few names that are familair to you from your build process output -- some of the magic is revealed.

The fix is to comment out a line.

1. Open the ServiceBuilderMojo file

2. Go to line number 252

3. Comment out the line you find there so that it looks like this

...
else {
	hbmFileName = webappResourcesDir.concat("/META-INF/portlet-hbm.xml");
	jsonFileName = webappDir.concat("/js/service.js");
	modelHintsFileName = webappResourcesDir.concat("/META-INF/portlet-model-hints.xml");
	ormFileName = webappResourcesDir.concat("/META-INF/portlet-orm.xml");
	//serviceFileName = webappDir.concat("/WEB-INF/service.xml");
	springBaseFileName = webappResourcesDir.concat("/META-INF/base-spring.xml");
	springClusterFileName = webappResourcesDir.concat("/META-INF/cluster-spring.xml");
...	
NOTE: depending on the version you are working with - the line number that you need to comment out might change. The code is the same though so you should be able to just search for the line referenced above. You'll know if you got it right because if you didn't comment out the right line, you'll get an error saying it can't find your service.xml file as you can see in the JIRA issue referenced earlier.

Why do we comment this out. Well, if you used a debugger to step through this code you'll find that when it runs, it does pick up the parameter you pass, but this line overrides the parameter with the default location /WEB-INF/service.xml. And if you don't pass a value? this doesn't break it -- the absence of the paramter assigns the correct value which means bascially this line is unecessary. If you have the time, you should study this file for a few minutes as it can open your eyes to a lot of service builder magic that is not really something that is advertised. You might even find an out of the box way to address something that you are --- ummm "creatively" solving right now :)

4. When you are done, Save the file and close it.

5. Next we're going to build and install the plugins (locally). For me, on the command line I go to LIFERAY_MAVEN_SUPPORT where we see a pom.xml. There I can use the standard maven stuff.

$>mvn clean install

Once it is done, you can validate in your maven local repo that the libraries are there. When you are satisfied that you have patched and installed the plugins, move on to putting them in use.

 

Using Multiple service.xml Files


I'm not going to go through how to create a portlet here. I'm going to assume that you already know how to do that, or that you have a plugin you want to apply this to already. Obviously, it needs to be a service builder plugin type. My structure looks like this --

└── sample-service-builder
    ├── pom.xml
    ├── sample-service-builder-portlet
    │   ├── pom.xml
    │   └── src
    │       └── main
    │           ├── java
    │           ├── resources
    │           │   └── portlet.properties
    │           └── webapp
    │               ├── css
    │               │   └── main.css
    │               ├── icon.png
    │               ├── js
    │               │   └── main.js
    │               ├── view.jsp
    │               └── WEB-INF
    │                   ├── liferay-display.xml
    │                   ├── liferay-plugin-package.properties
    │                   ├── liferay-portlet.xml
    │                   ├── portlet.xml
    │                   ├── service.xml
    │                   └── web.xml
    └── sample-service-builder-portlet-service
        └── pom.xml

1. Let's start by adding the packages that we referenced above. Go to the /sample-service-builder/sample-service-builder-portlet/src/main/java and add the hierarchies so that you have

  1. ../src/main/java/com/university/course
  2. ../src/main/java/com/university/student
  3. ../src/main/java/com/university/professor

2. Next we're going to add a service.xml file to each location. Now I have --

  1. ../src/main/java/com/university/course/service.xml
  2. ../src/main/java/com/university/student/service.xml
  3. ../src/main/java/com/university/professor/service.xml

3. Now put what you like in each of these files. Here is what I put in my course service.xml and I basically used the same structure in the student and professor ones, but changed the package and entity names accordingly. So I am giving the full file for course, but a partial for the others.

 

../course/service.xml

<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">

<service-builder package-path="com.university.course">
    <namespace>UNIVERSITY</namespace>
    <entity name="Course" uuid="true" local-service="true" remote-service="false" cache-enabled="true">

        <!-- PK fields -->
        <column name="courseId" type="long" primary="true"/>

        <!-- Group instance -->
        <column name="groupId" type="long"/>

        <!-- Audit fields -->
        <column name="companyId" type="long"/>
        <column name="userId" type="long"/>
        <column name="userName" type="String"/>
        <column name="createDate" type="Date"/>
        <column name="modifiedDate" type="Date"/>

        <!-- Other fields -->
        <column name="name" type="String"/>
    </entity>
</service-builder>

../student/service.xml

<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">

<service-builder package-path="com.university.student">
    <namespace>UNIVERSITY</namespace>
    <entity name="Student" uuid="true" local-service="true" remote-service="false" cache-enabled="true">

    ...
</service-builder>

../professor/service.xml

<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">

<service-builder package-path="com.university.professor">
    <namespace>UNIVERSITY</namespace>
    <entity name="Professor" uuid="true" local-service="true" remote-service="false" cache-enabled="true">

    ...
</service-builder>

4. With this in place, we have all that we need to generate our code. Now, you can run it using the command line, but honestly, I struggle to remember all the commands so I like to add profiles to my pom so that I can just run the standard maven command to trigger service builder. Open the /sample-service-builder/sample-service-builder-portlet/pom.xml

5. After the block outlining the <build> add the following chunk of code.

<profiles>
      <profile>
          <id>build-course</id>
          <activation>
              <property>
                  <name>build-course</name>
              </property>
          </activation>
          <build>
              <plugins>
                  <plugin>
                      <groupId>com.liferay.maven.plugins</groupId>
                      <artifactId>liferay-maven-plugin</artifactId>
                      <version>${liferay.maven.plugin.version}</version>
                      <executions>
                          <execution>
                              <phase>generate-sources</phase>
                              <goals>
                                  <goal>build-service</goal>
                              </goals>
                              <configuration>
                                  <serviceFileName>
                                      ${basedir}/src/main/java/com/university/course/service.xml
                                  </serviceFileName>
                              </configuration>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </build>
      </profile>
      <profile>
          <id>build-student</id>
          <activation>
              <property>
                  <name>build-student</name>
              </property>
          </activation>
          <build>
              <plugins>
                  <plugin>
                      <groupId>com.liferay.maven.plugins</groupId>
                      <artifactId>liferay-maven-plugin</artifactId>
                      <version>${liferay.maven.plugin.version}</version>
                      <executions>
                          <execution>
                              <phase>generate-sources</phase>
                              <goals>
                                  <goal>build-service</goal>
                              </goals>
                              <configuration>
                                  <serviceFileName>
                                      ${basedir}/src/main/java/com/university/student/service.xml
                                  </serviceFileName>
                              </configuration>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </build>
      </profile>
      <profile>
          <id>build-professor</id>
          <activation>
              <property>
                  <name>build-professor</name>
              </property>
          </activation>
          <build>
              <plugins>
                  <plugin>
                      <groupId>com.liferay.maven.plugins</groupId>
                      <artifactId>liferay-maven-plugin</artifactId>
                      <version>${liferay.maven.plugin.version}</version>
                      <executions>
                          <execution>
                              <phase>generate-sources</phase>
                              <goals>
                                  <goal>build-service</goal>
                              </goals>
                              <configuration>
                                  <serviceFileName>
                                      ${basedir}/src/main/java/com/university/professor/service.xml
                                  </serviceFileName>
                              </configuration>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </build>
      </profile>
</profiles>
NOTE: I also have added properties to the root projects pom specifying the versions of liferay and java that I want to use. If you don't specify the versions of Liferay you will get an error, but that has nothing specifically to do with what we're doing here.

What have we done? Nothing earth shattering. We just added a few profiles, one for each service.xml file. We use a common naming pattern so that build-* where the * we can substitude with course, student and professor. Perhaps this is not the most elegant solution but it works for me and is portable to other team members.

6. Alright, last step. A few command line commands to get the code to be generated.

$>mvn -P build-course generate-sources
$>mvn -P build-student generate-sources
$>mvn -P build-professor generate-sources
		

7. I'm not going to paste the entire tree. But you can see from this snapshot that I have individual packages for my entities.

sample-service-builder-portlet-service
      ├── pom.xml
      └── src
          └── main
              └── java
                  └── com
                      └── university
                          ├── course
                          │   ├── model
                          │   │   ├── CourseClp.java
                          │   │   ├── ...
                          │   │   └── CourseWrapper.java
                          │   ├── NoSuchCourseException.java
                          │   └── service
                          │       ├── ClpSerializer.java
                          │       ├── ...
                          │       ├── messaging
                          │       │   └── ClpMessageListener.java
                          │       └── persistence
                          │           ├── ...
                          │           └── CourseUtil.java
                          ├── professor
                          │   ├── model
                          │   │   ├── ProfessorClp.java
                          │   │   └── ...
                          │   ├── NoSuchProfessorException.java
                          │   └── service
                          │       ├── ClpSerializer.java
                          │       ├── messaging
                          │       │   └── ClpMessageListener.java
                          │       ├── persistence
                          │       │   ├── ...
                          │       │   └── ProfessorUtil.java
                          │       ├── ...
                          │       └── ProfessorLocalServiceWrapper.java
                          └── student
                              ├── model
                              │   ├── StudentClp.java
                              │   ├── ...
                              │   └── StudentWrapper.java
                              ├── NoSuchStudentException.java
                              └── service
                                  ├── ClpSerializer.java
                                  ├── messaging
                                  │   └── ClpMessageListener.java
                                  ├── persistence
                                  │   ├── ...
                                  │   └── StudentUtil.java
                                  ├── ...
                                  └── StudentLocalServiceWrapper.java

			

 

Conclusion


So why does this matter, and why would you bother. Honestly, it probably doesn't matter much, and most people probably wouldn't bother. I do it because I like the separation in the packages but it's almost purely aesthetic. I suppose you could argue that, if you have project with A LOT of entities, that it will build faster since you only build the relevant entities, but I'm not sure that the time savings would be so drastic that it's a reason. If it is, then you probably should take a look at how you project is structured :). In truth I went down this path primarily for curiosity and to learn something new. Having found it, I now basically use this model exclusively, even if I only need one package in my plugin and thought maybe someone else might want to try it as well. Are you going to be crowned the hero of your team? or warrant a little extra on your bonus for using this? probably not.

What about Liferay 7. Well, in truth I haven't tried this on 7 yet. Mostly because of the lack of time but also because the architecture for 7 is such that I find myself using the "plugin per entity" type solution rather than clumping them together. I'm also not sure if/how you would do this with gradle, or ANT for that matter -- but I am willing to bet it is possible. Maybe I'll put it on my list and, if I get that far down my list, update this post with what I find.

Arquillian + Maven

Technical Blogs August 3, 2015 By Andrew Jardine

My very first blog post. To be honest I've said for months that I was going to start doing this but there just always seems to be something more "urgent". Today I promised someone on the forums that I would write this one -- so maybe this will get the ball rolling for me.

DISCLAIMER: I did this several months ago so I am not sure how much has changed since then. It worked great for all the Liferay services, but it fell short for the part where I needed to mock PortletRequests etc. It is certainly possible, but there is more leg work required for that. I haven't run that marathon yet, so it won't be in this post (if that is what you are looking for). For the Liferay services though, Arquillian is a dream -- buh-bye PowerMock! 

Overview

Most of the stuff that I have found out there for Arquillian is related to using the plugins SDK and ANT. There will always be a soft spot in my heart for the Plugins SDK but in recent months my knowledge and, as a result, appreciation for Maven has really come to light. This post outlines the steps that I took to get Arquillian up and running for one of my (portlet) plugin projects.

There are a few steps.

  1. Configure your users in Tomcat
  2. Add a Connection for Arquillian
  3. Build the Maven Dependencies
  4. Configured out Project to use Arquillian
  5. Write a Unit Test

So.. let's get started.

 

Adding Users to Tomcat

In order for Arquillian to do its job, we need to give it some level of control to TOMCAT. When the unit tests are executed, Arquillian will actually deploy the plugin inside the container – which is how it has a runtime with which it can locate your dependencies from the test.

1. Go to your $TOMCAT_HOME/conf directory and open the tomcat-users.xml file. If none exists, create it. 

2. Add the following to the file.

3. Save and close the file.

 

Add Connection for Arquillian

Since Arquillian is going to use the manager application of Tomcat to deploy the plugin as part of the test execution, we need to modify our environment properties to support it.

1. Go to your $TOMCAT_HOME/bin directory and open the setenv.sh (if you are on MAC/Linux) or setenv.bat (if you are on Windows)

2. Add the following to the file.

NOTE: You can of course alter your port to use a different value other than 8099 – but this is the "default" value.

3. Save and close the file.

4. Download the tomcat manager application and upack it into the TOMCAT_HOME/webapps directory

5. Restart your TOMCAT server to make sure the changes are picked up.

 

Build Maven Dependencies

Right now the Arquillian support is, technically, has been released to the Community Edition of Liferay. On top of this, it was done first and foremost for ANT with a port to Maven coming after. It's a little tricky to get going but here is what I did. 

1. Add the following mirror to your local maven settings.xml

2. Save and close the file.

3.Go to a location on your drive where you download git projects. I use /opt/git as a root for anything I clone.

4. At the command line type: git clone https://github.com/arquillian/arquillian-extension-liferay

5. Once the repository has been cloned, go into the  arquillian-extension-liferay directory

6. Execute the maven install goal with: mvn install– which should build the library and place it into your local repo.

 

Configure Our Project to use Arquillian

Almost there, just a couple steps to go. Naturally, in order to use Arquillian we need to tell our project about its existence. First and foremost we'll need to add the dependencies to our pom.xml – but most likely this will eventually more to the master parent pom.xml file so that you don't have to include it every time you create a new plugin. Second we're going to create a configuration file to tell Arquillian where to find out Liferay server and what the credentials are. It must go in a specific location AND that location must be listed as a SOURCE folder for the project. 

1. Open the pom.xml for your project, or if you have one that you use locally, the parent pom.xml for all your projects, and add the following dependency management node just above your listed dependencies.

2. Add the following dependencies.

 

3. Save and close the file.

4. Now add a folder called integration under the /src/test folder.

5. In there create a new file called arquillian.xml and place the following contents (adjusting values based on your settings).

6. Save and close the file.

7. Now just double check to make sure that it is listed as a source folder. Click on the name of your project and then hit ALT-ENTER. You should see this --

8. Click on the tab marked Source.

9. Check to see if there is a reference to the /test/integration folder. If there is not, then click the Add ... button, and select the integration folder.

 

 

Write a Unit Test

1. Click on the /test/java folder and create a new test class – I called mine ArquillianPortletTest (because my project was named arquillian-portlet).

2. Make the body of the class the following, adjusting the name and package to whatever your class is.

Note that all I have done is used the syntactic sugar that is provided by Arquillian by marking my test class with an annotation to tell the runtime that this test should run inside the Arquillian container. What we don't see here is that the Liferay dependency automatically provides us with the ShrinkWrap we need that contains the portal services. If you look at the Arquillian documentation you will see that normally you need to write a method decorated with @Deployment that will be used to create an archive with your dependencies (but not mocks) – Liferay does this for us so all we have to do is write our tests!

3. Save the file.

4. Right click on the class and choose Run As > JUnit Test and you should get a green bar. No mocking the CounterLocalServiceUtil or the UserLocalServiceUtil.

Showing 3 results.