Monday, 17 March 2014

New Eclipse project connected to Git repository

You have a new Eclipse project to work on and you want to version control it on a Git repository on another machine. Here's how you do it.

1. On the server you wish to have your Git repository remotely stored, setup an empty repository. In our examples here the repository and project will be called UVaOnline

johan@htpc ~/gitrepos $ pwd
/home/johan/gitrepos
johan@htpc ~/gitrepos $ mkdir UVaOnline.git
johan@htpc ~/gitrepos $ cd UVaOnline.git/
johan@htpc ~/gitrepos/UVaOnline.git $ git --bare init --shared=all

2. Add a file to this empty project if you wish to make sure there is something to check out from Eclipse.

johan@htpc ~/gitrepos $ cd /tmp
johan@htpc /tmp $ git clone /home/johan/gitrepos/UVaOnline.git/
johan@htpc /tmp $ cd UVaOnline/
johan@htpc /tmp $ touch FOO
johan@htpc /tmp $ git add FOO
johan@htpc /tmp $ git commit
johan@htpc /tmp $ git push origin master


3. In Eclipse, add the Git Repository Exploring perspective via the menu Window -> Open Perspective.


4. Click the "Clone a Git repository and add the clone to this view" icon in the perspective.

5. In the first step of this wizard, enter the connection details for the Git repository. In my case I use my SSH user to log on the server.


6. If your project was emtpy, you only have the master branch to work with. Click next.


7. Choose where you wish to have your local Git repository on the machine Eclipse is installed on. I selected the "Import all existing projects after clone finishes".


8. Now a new repository should be available in the Git repository Explorer.


9. Try to fetch the repository if you want to see the refs configurred.


10. You see the connection URL again, next.


11. Here you could add or delete refs pointers. For our purposes, this looks alright.


12. To import the project into the Java/Java EE perspectives, click the "Import Projects" option in the menu.


13. Since this project is a bare bones project with no .project file with Java facets, we must select to import the project as a "Import as general project".


14. Probably the default name is good enough.


15. Now you could start adding file to your project. I made my project a Maven project here and added some source code. Once you're ready to commit to the remote repository, right-click and choose Team -> Synchronise Workspace.


16. In the next view, select the files you wish to commit and add them to Git index first.


17. Then right click again and commit.



18. If you do not click "Commit and push" the files are only added to your local git repository on the same machine. That can be good if you want to pile up a bunch of local commits before pushing to the remote repository. When you want to push all local commits, in the Java perspective right click and go via Remote to Push.


19. If it is the first time, you might need to add a spec for this. If you work on master which you do if you have followed the tutorial, simple add "refs/heads/master" as source and destination ref and click "Add spec".


20. New spec added.


21. Once finished, the commits you had locally are pushed to the remote repo.


Thats it. I have some more stuff on working with Git in Eclipse in this blogpost about Github cloning

For more advanced Git usage, you might need the console. That's no problem, simply make sure to refresh the Eclipse project after working in the console since Eclipse must refresh the .git folder etcetera. See this Git cheat sheat for my favourites.

Tuesday, 18 February 2014

Porthello Legends - Android Reversi Port

I wrote a clone of the game Reversi/Othello for Android three years ago and it actually made some progress on Google Play (Market as it was called then). It was downloaded nearly 1000 times and a lot of players seems to have enjoyed it because I could see in the highscore lists and the Analytics statistics that they spent quite some time on trying to beat the AI to gain access to even harder levels.

Unfortunately, the name Othello Legends was a bad choice since it was trademarked. That meant that after 6 months the game was suspended from Google Play. Since I put quite a lot of effort into the game it is a shame that it disappeared and I have now done a refactoring to the new name Porthello Legends :-).



My Android tablet died last summer and all I got to test with is an pretty old HTC Desire which the game seems to work fine on. It would be interesting to get feedback on how the game performs on newer phones/tablets as well.

Check out the game at Google Play, search for "Porthello Legends" or go to https://play.google.com/store/apps/details?id=se.noren.android.porthello&hl=en.



The old blogpost on the inital release can be found here http://macgyverdev.blogspot.se/2011/12/othello-legends-10-in-android-market.html.

Tuesday, 11 February 2014

Apache web server as reverse proxy and virtual host for Tomcat 7

I'm running a couple of sites on a Linux server on my local network. I only have one public ip address but I want to be able to host multiple sites on my servers. So this can be done by using a reverse proxy in front of the application servers which delegates traffic depending on which host name the end user was trying to request.

I've implemented this by using the reverse proxy feature with virtual hosts in the Apache HTTP web server.
The result this exercise is trying to achieve is that external traffic from internet requesting pages from the site www.all-about-units.com which is DNS mapped to my public ip address will be routed to the Apache web server on the local network via a port forwarding rule in the router on the local network. The Apache web server will look into its rules for virtual hosts and see that the user requested the domain www.all-about-units.com and therefore delegate the traffic to a separate Apache Tomcat 7 servlet container running on the local network to serve the request. The traffic is then forwarded back through the web server to the end user.

Setup Apache HTTP server as reverse proxy

First of all the Apache HTTP web server must be installed. I had mine installed since long ago but if I remember correctly there's not much than doing this is you have a Debian/Ubuntu/Mint like Linux server

sudo apt-get install apache2

Now, to enable the reverse proxy feature of the Apache server

/ $ sudo a2enmod proxy_http

Next, we must create a configuration for our new site as a virtual host. Go to the directory of available sites

/etc/apache2/conf.d $ cd /etc/apache2/sites-available/

Copy the default configuration to a setup for your site

/etc/apache2/sites-available $ sudo cp default all-about-units

Lets edit the configuration file. The first line tells the Apache server to listen on port 80, the standard HTTP port. Next the names of the virtual host are given. So traffic requesting another domain name or the ip address directly will not be handled by this virtual host configuration.

Next look att the ProxyPass and ProxyPassReverse, these tell Apache where to forward the request that has matched this virtual host rule. In this case to the Tomcat server on the same machine (127.0.0.1 is the loopback address of localhost) but on port 8080. The slash before the address means that all requests should be forwarded to the  Tomcat server. If for example you had ProxyPass /foo/bar/ http://127.0.0.1:8080/ then the request www.all-about-units.com/foo/bar/page.html would be redirected to 127.0.0.1:8080/page.html

/etc/apache2/sites-available $ sudo vim all-about-units
<VirtualHost *:80>
  ServerName all-about-units.com
  ServerAlias www.all-about-units.com
  ProxyRequests Off
  ProxyPreserveHost On
  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

Enable and disable the site

To tell the Apache HTTP web server to enable this configuration, run the a2ensite command on the name of the new configuration

/etc/apache2/sites-available $ sudo a2ensite all-about-units
Enabling site all-about-units.
To activate the new configuration, you need to run:
  service apache2 reload

Reload Apache

/etc/apache2/sites-available $ sudo service apache2 reload
 * Reloading web server config      

I had some issues so I restarted Apache the hard way also and then it works.

/etc/apache2/sites-available $  sudo /etc/init.d/apache2 restart
 * Restarting web server apache2                                                          apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName
 ... waiting apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName

That's it!

Verify in the logs of the application server that the request are directed correct and your home.
Next step might be to enable the mod cache feature of Apache to let the web server handle caching of all static material served by the application server like css, images and javascript. More on that in another post.

If you for some reason want to take the site of the web for some time you can instead of stopping the application server disable the site in the web server virtual host forwarding by using the command

sudo a2dissite all-about-units

Tuesday, 4 February 2014

How to make Jenkins install the packaged war in a Tomcat 7

This is a sequel to http://macgyverdev.blogspot.se/2014/01/setup-jenkins-project-from-git.html where I set up Jenkins to build my latest project. The next step is to install the war-file built by Jenkins on my Tomcat 7 server.

First of all we must make sure Tomcat accepts installations by scripting instead of the human html GUI version. In the tomcat-users.xml file, located in something similar to this:

/var/lib/tomcat7/conf/tomcat-users.xml

Make sure that the admin user, whatever his name is, has the role manager-script. Here's the important part of my file:

<tomcat-users>

<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<role rolename="admin"/>

<user username="admin" password="somethignsecret" roles="manager-gui,admin-gui,manager,admin,manager-script,admin-script"/>

</tomcat-users>

Now, in Jenkins you must install the Deploy Plugin. You find it in the plugins section.



Your Jenkins job is here assumed to be configured to package a war file. You can verify this by inspecting that a build generates a war file like this:

~/.jenkins/jobs/UnitConversion/workspace/target/Unitconversion.war

For this Jenkins job, go to the settings page and add a new post-build action. Choose the "Deploy war/ear to a container".

In this view make sure the path to the packaged war is relative to the workspace area of the job. Most probably it will be "target/yourapp.war".


Now, rebuild your job and when inspecting the console logs you should see that the war/ear is installed!

Deploying /home/johan/.jenkins/jobs/UnitConversion/workspace/target/Unitconversion.war to container Tomcat 7.x Remote
  Redeploying [/home/johan/.jenkins/jobs/UnitConversion/workspace/target/Unitconversion.war]
  Undeploying [/home/johan/.jenkins/jobs/UnitConversion/workspace/target/Unitconversion.war]
  Deploying [/home/johan/.jenkins/jobs/UnitConversion/workspace/target/Unitconversion.war]
Finished: SUCCESS

This way it takes two clicks from editing the code in Eclipse to update the site All About Units in production, first commit and push to Git, second start the Jenkins job.

One note, if you have deployed your application with context root "/" in Tomcat as I have done, see http://macgyverdev.blogspot.se/2014/02/how-to-change-context-root-to-in-tomcat.html, you can not have Context path = "/" in the configuration above. Instead, keep multiple context roots in Tomcat, and as in my example add the context root with a longer name, otherwise the deploy plugin cannot undeploy the ROOT web application. If you according to my blog post on it has manipulated the server.xml a reinstallation of "/webappname" will also reinstall the ROOT application since they point to the same directory on disk.

How to change context root to / in Tomcat 7

Here's my problem, I have a new project going which will be deployed as http://www.all-about-units.com/en. It's a site for general facts and info about units. It's a Java web application running on a Tomcat 7 servlet container. It is automatically deployed to the context root "Unitconversion" since that is the name of the WAR-file when it is uploaded via the admin ui.
That means I will access it as

http://myserver:port/Unitconversion

I want it to be deployed as context root "/" so that the URL becomes this in production

http://myserver:port/

My Tomcat 7 is running on a Linux Mint server but I guess these instructions are the same regardless of operating system. Here's how I managed to get it working.

First I uploaded my new web application in the Tomcat manager admin GUI. We got something like this now.


Now, if you inspect the file system you should have your app deployed at  /var/lib/tomcat7/webapps.

johan@johanhtpc /var/lib/tomcat7/webapps $ ll
total 31040
drwxrwxr-x  5 tomcat7 tomcat7     4096 feb  4 20:22 .
drwxr-xr-x  6 root    root        4096 dec 18 19:25 ..
drwxr-xr-x  3 root    root        4096 dec 18 19:25 ROOT
drwxr-xr-x 10 tomcat7 tomcat7     4096 feb  4 20:22 Unitconversion
-rw-r--r--  1 tomcat7 tomcat7 23617897 feb  4 20:22 Unitconversion.war
drwxr-xr-x  8 tomcat7 tomcat7     4096 dec 28 12:44 WeatherStationServer
-rw-r--r--  1 tomcat7 tomcat7  8140647 dec 28 12:44 WeatherStationServer.war

The hacky way now would be to change the ROOT application with my new code, a simple overwrite with my web app. But that doesn't smell so good...

So what you do is that you edit

/var/lib/tomcat7/conf/server.xml

and add these lines inside the <Host> element. Change "Unitconversion" to your application name.

<Context path="" docBase="Unitconversion">
   <!-- Default set of monitored resources -->
   <WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
<Context path="ROOT" docBase="ROOT">
   <!-- Default set of monitored resources -->
   <WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>

This will make the default root application appear under context root "/ROOT".
Restart Tomcat to make changes take place.

sudo /etc/init.d/tomcat7 restart

And now the app is accessible at "/" and at the old context root "Unitconversion" as well. This is also seen in the GUI if you refresh the manager.



Tuesday, 28 January 2014

Java 8 in Eclipse

Have you tried out Java 8 yet? The official release is not until March 2014 but there have been a set of release candidates to play with. It is actually very easy to get up and running.
  1. Download a JDK 8 snapshot from https://jdk8.java.net/ and install it.
  2. Install a version of Eclipse that can handle Java 8 syntax. I downloaded Eclipse Luna 4.4 from http://downloads.efxclipse.org/eclipse-java8/2013-05-19/.
To get started, create a new project in Eclipse and make sure you have chosen the Java 8 JRE.



Here's a class that uses a lambda expression to instantiate the functional interface PowerCalculator. A functional interface is an interface with only one abstract method.

public class TestClass {

    interface PowerCalculator {
        int pow(int n, int exponent);
    }

    public static void main(final String[] args) {
        final PowerCalculator calc = (int n, final int exponent) -> {
            return (int) Math.pow(n, exponent);
        };
        
        int product = calc.pow(3, 4);
        System.out.println(product);
    }
}
This outputs the expected "81".

That was maybe not very cool, but when looking into the more functional style of programming you can do with lambda expressions, this is the most interesting Java upgrade since Java 5 in my opinion.
I wrote an article in 2012 on FP in Java http://macgyverdev.blogspot.se/2012/10/functional-programming-in-java.html and took a quick look now again to see whether the original spec still is valid.

Here is a few examples I tried on the Collections framework. There has been changes since the 2012 draft. For example is the stream() interface added, previously you could do a filter() directly on the collection. The stream interface allows for parallel execution and

public class TestClass {

    public static void main(final String[] args) {
     List<Animal> animals = Arrays.asList(new Animal(10), new Animal(20), new Animal(30), new Animal(40));
     
     // Each animal eats another calorie
     animals.forEach(a -> a.eat(1));
     
     // Print animal
     animals.forEach(a -> a.print());
     
     // Print only the animals which have more than 30 calories
     animals.stream()
            .filter(a -> a.nutrition() > 30)
            .forEach(a -> a.print());
     
     // Calculate total sum of all animals calories
     int sum = animals.stream()
                      .mapToInt(a -> a.nutrition())
                      .sum();
    }
}

class Animal {
 private int calories = 0;
 
 public Animal(int calories) {
  this.calories = calories;
 }
 
 public void eat(int calories) {
  this.calories += calories;
 } 

 public void print() {
  System.out.println("Calories: " + calories);
 } 

 public int nutrition() {
  return calories;
 }
}


The Eclipse support in Luna 4.4 is pretty good. But when using the command completition it is not obvious what the different lamda expression consumers expect. I guess once you have played around with it some more you get accustomed to the Predicate and Consumer APIs.

Friday, 24 January 2014

Setup Jenkins project from Git repository with Findbugs, Cobertura and Checkstyle

I created a Jenkins setup for my current Java hobby project (a site on physical units intended to make ads money, check it out at http://www.all-about-units.com) yesterday and noticed that my old note on how to setup Jenkins (then it was written for Hudson) is a bit out of date. The Jenkins configuration menues for generating Findbugs static analysis, Cobertura code coverage and Checkstyle format control were completely new. In addition to that, I've migrated to Git since then so there are a few steps also involved in getting Git running as code repository within Jenkins.

Here's a workflow for setting up Jenkins with these features.

Install Jenkins and plugins

First of all, if you haven't installed Jenkins the easiest way is to download the war archive from http://jenkins-ci.org/. This article is based on Jenkins version 1.532.1. You can install in in a server container like Tomcat or Jetty, but the easiest way to get started is to run it from the command line like 

java -jar jenkins.war

I created a small bash script which also changes the port since I have another server using the default 8080 port.

#!/bin/sh

nohup java -jar jenkins.war --httpPort=8081

So now you can access Jenkins via http://localhost:8081 or similar.

Now we must install some plugins which are not part of the core distribution. Go to the settings of Jenkins and find the Plugins section.

Here you should at least find the following plugs and install them





Creating Jenkins job

Create a new Jenkins job. My project handles its dependencies via Maven 3 which suits Jenkins very well. Choose the Maven 2/3 option.


In the next step, choose Git as SCM. If you have your Git repository on the same server as Jenkins you can simply add the file path to the repository. Otherwise you setup the security credentials here as well. Also, choose if you want to build master or some other branch.


We haven't talked about the pom.xml for the project yet, but it will be configured to use some Maven goals. So in the Maven goals section, add goals for test package site.

Under Build settings, check the boxes for Publish Checkstyle analysis resultsPublish FindBugs analysis results and Publish duplicate code analysis results.


In the Post-build Actions add a step for Cobertura reporting.


Maven will generate the Cobertura report to the Jenkins workspace under path /target/site/cobertura/coverage.xml so add this in the configuration of the action.



Now you should have a Jenkins job setup ready for building.


Before building we must make sure the pom.xml of the project has the correct plugins and reports configured.

Maven pom.xml configuration

After all the dependency specifications in the pom.xml add something similar to this. In this setup Cobertura is configured in the package phase and that's why we added that Maven goal above. If you hadn't added this in the pom file, you could have added separate Maven goals for Cobertura in Jenkins as well.

<build>
   <finalName>Unitconversion</finalName>
   <plugins>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <version>${findbugs.version}</version>
      </plugin>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>cobertura-maven-plugin</artifactId>
         <version>${cobertura.version}</version>
         <configuration>
            <formats>
               <format>xml</format>
            </formats>
         </configuration>
         <executions>
            <execution>
               <phase>package</phase>
               <goals>
                  <goal>cobertura</goal>
               </goals>
            </execution>
         </executions>
      </plugin>
   </plugins>
</build>
<reporting>
   <plugins>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <version>${findbugs.version}</version>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
         <version>${checkstyle.version}</version>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-report-plugin</artifactId>
         <version>${surefire.reportplugin.version}</version>
      </plugin>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>cobertura-maven-plugin</artifactId>
         <version>${cobertura.version}</version>
         <configuration>
            <formats>
               <format>xml</format>
            </formats>
         </configuration>
      </plugin>
   </plugins>
</reporting>
Run the job and everything should work out of the box. Do another build to start getting history graphs like below of the trend of failing tests, checkstyle warnings, issues found by Findbugs and te code coverage statistics from Cobertura.