Thursday, November 29, 2012

Maven dependency unpacking filtering in custom assemblies

Introduction

I have been using Maven assemblies for a while to do some relatively simple building of artifacts which Maven does not build out of the box.  Assemblies are quite powerful and can do a lot for you. I ran into a problem with dependency unpacking. I needed some of the unpacked files to be filtered so as to replace ${variablename} inside the files but found this feature not supported.  The purpose of this article is to demonstrate how dependency unpacking filtering can be done when using custom Maven assemblies.

Of course remember that the plugins attached to a particular <phase> are executed by Maven in the order you have them in our pom - put the plugins in the wrong order and you won't get the right results.

1. Use maven-dependency-plugin to get the dependency unpacked.

First  you want to use the maven-dependency-plugin to unpack your dependency. Unpack inside the /target directory for use later.  The XML below shows configuration to do this.

1:  <plugin>  
2:    <groupId>org.apache.maven.plugins</groupId>  
3:    <artifactId>maven-dependency-plugin</artifactId>  
4:    <version>2.6</version>  
5:    <executions>  
6:     <execution>  
7:      <id>unpack-dependencies</id>  
8:      <phase>prepare-package</phase>  
9:      <goals>  
10:       <goal>unpack-dependencies</goal>  
11:      </goals>  
12:      <configuration>     
13:       <includeArtifactIds>SomeArtifactId</includeArtifactIds>         
14:       <outputDirectory>${project.build.directory}/SomeArtifactId</outputDirectory>             
15:      </configuration>  
16:     </execution>  
17:    </executions>  
18:   </plugin>  


In the code above:

Line #8 links this plugin configuration to the prepare-package phase, which is typically what you will want to do since you need to unpack your dependencies before the package phase runs your custom assembly

Line #10 sets the goal to unpack-dependencies, which of course means the dependency will be unzipped.

Line #13 is the <includeArtifactIds> tag which can take a comma-separated list of <dependency><artifactId> id values. I typically like to use only 1 value though and if I need to unpack multiple dependencies I create multiple plugin configurations.

Line #14 is the <outputDirectory> tag and this specifies where the dependency is unzipped.  Typically, just dump it into some /target subdirectory.

2. Use maven-assembly-plugin to build your assembly.

Next you will need to configure the maven-assembly-plugin to execute during your build. Of course put this plugin configuration AFTER the maven-dependency-plugin configuration so Maven executes them in the right order. The XML below shows configuration to do this:

1:        <plugin>  
2:          <artifactId>maven-assembly-plugin</artifactId>  
3:          <executions>  
4:            <execution>  
5:              <configuration>  
6:                <descriptors>  
7:                  <descriptor>my_assembly.xml</descriptor>  
8:                </descriptors>  
9:              </configuration>            
10:              <id>create_directory</id>  
11:              <phase>prepare-package</phase>  
12:              <goals>  
13:                <goal>single</goal>  
14:              </goals>  
15:            </execution>  
16:          </executions>  
17:        </plugin>  


In code above:

Line #7 says to use the assembly defined in your my_assembly.xml file.

Line #11 links this plugin configuration to the prepare-package phase.  This is the same phase the maven-dependency-plugin configured above is linked to. Maven will run this maven-assembly-plugin configuration after the maven-dependency-plugin configuration as long as have them in that order in the pom.

The my_assembly.xml file is the configuration for your custom assembly.  To work with the unpacked dependency files, you refer to them just like you would any other file.  The XML below shows configuration to do this.

1:  <fileSet>  
2:        <directory>${project.build.directory}/SomeArtifactId</directory>  
3:        <outputDirectory>/where/to/put/it/in/your/assembly</outputDirectory>  
4:        <includes>  
5:          <include>file-to-be-filtered.txt</include>          
6:        </includes>  
7:        <filtered>true</filtered>  
8:  </fileSet>  


As you can see, you can use the <filtered> option to make sure the files are filtered. Of course, use multiple configurations if you need to filter some files but not others.

In the code above:

Line #2 says to use the files which were unpacked by the maven-dependency-plugin

Line #3 says where to put the files in your assembly

Line #5 says what files are to be included.

Line #7 says to filter the files.  This will of course replace the ${variablename} strings

Summary

That's it, enjoy

Friday, July 6, 2012

SVN Username Mysteriously Changes on Windows

Introduction

While using SVN, I recently noticed my commits to the repository were no longer using my computer login username.  Somehow, something happened to tell SVN to use a different username.  If you are using Windows for you development environment and you have run into this issue, this article will give you hints on where to look to fix it.

Subversion's "Application Data"

I am running the Window's SVN client SlikSVN v1.6.1.  After running the Maven release plugin on my project, I noticed commits to the SVN repository were no longer using my computer login username, but were instead using the username associated with running the Maven release plugin.  Since not all users are given Nexus permissions to write releases, the Maven release plugin needs to be executed with a different username.  SlikSVN somehow "remembered" this username and all commits to the SVN repository after that were using it.  This is of course a major problem because looking back through the SVN history it is not possible to determine who really made the changes.

After searching online for this problem, I spent some time running through the "Application Data" directory.  I eventually found this directory:
XP: "C:\Documents and Settings\[username]\Application Data\Subversion\auth\svn.simple"
7: "C:\Users\[username]\AppData\Roaming\Subversion\auth\svn.simple"
Inside this directory is what looks like a randomly generated file:
42a1ce589862a9b8acde6133f937c22d
With contents that looks like this:
K 8
passtype
V 8
wincrypt
K 8
password
V 280
[SOME_ENCRYPTED_PASSWORD]
K 15
svn:realmstring
V 28
<http://bjcesinf03:80> crowd
K 8
username
V 14
[SOME_SVN_USERNAME]
END

The [SOME_SVN_USERNAME] was my problem. 

The solution is to delete this file.

Summary

If you are running Windows and your SVN username mysteriously becomes something else when performing commits to the repository, try going to this directory:
 "C:\Documents and Settings\[username]\Application Data\Subversion\auth\svn.simple"
and delete the randomly generated file in this directory which contains the username.

Enjoy!

Thursday, June 14, 2012

Spring Not Setting Cookie On AJAX Response

Overview

I was working on a problem where any request to a Spring Controller needed to have a Cookie set on its response.  Normal page to page navigation worked fine, but I noticed some (not all) of the of AJAX responses were missing the Cookie.  Even though I verified through log files and stepping through the code that the Cookie was being set on the Response, once the AJAX Response got back to the browser, there was no Cookie.  This article describes how I was able to configure Spring to always return a Cookie on a Spring Controller AJAX Response.

CookieInterceptor

A class was created in the project  which was responsible for setting the cookie on the Response.  Call  this class CookieInterceptor and have it implement the org.springframework.web.servlet.HandlerInterceptor interface.  A Spring Intecepter is similar to an Java EE Filter in that it allows the request and response to be intercepted and cross cutting concerns be added in the Interceptor instead of adding them to every Controller.  In my case, the cross cutting concern was to make sure the Response always had an updated value set for a Cookie.  I implemented HandlerInterceptor interface and added the code to set the Cookie in the postHandle() method.

Registering the Interceptor with Spring is easy:

<mvc:interceptors>
   <bean id="cookieInterceptor" class="com.widgit.CookieInterceptor"/>
</mvc:interceptors>

Testing

After creating CookieInterceptor and registering it with Spring, I performed some testing.  Navigating from page to page the Cookie value was found in the Response and the browser was getting the new Cookie as it should.

...however...

When testing AJAX calls, I got mixed results.  If  the Controller used the @ResponseBody  annotation to return the AJAX Response, I saw no Cookie in the Response.  However, if the Controller did NOT use @ResponseBody but instead returned a view string which was turned into a JSON object later, I did get the Cookie.  In all cases, I verified through log files and stepping through the code that the code for the CookieInterceptor was being called and the cookie was being set.  So why was one case getting the Cookie, and the other case not getting the Cookie?

Content-Type

A difference I saw in the responses had to do with the Response Content-Type.  When using the @ResponsBody annotation, the Response had a Content-Type="application/json".  However using other methods to return  the responses, the Response had a Content-type="text/html".  When the Content-Type was application/json no Cookie was set however when the Content-Type was text/html, the Cookie was there.  But why was this?

A Matter of Timing

At first I thought perhaps a Cookie could not be set on a Response of type application/json.  Research showed this was not the case.  So instead I turned to the CookieInterceptor.  The implementation had the Cookie setting code in the postHandle() method and even though this code was always being run, The Cookie was not being set when the Controller used @ResponseBody.  Through some research, my colleague Joseph Flatt thought this might be a matter of timing, meaning that for some reason when @ResponsBody was used to return the Response by the time you got back to running the code in the CookieInterceptor it was too late to make any changes to the Response: Perhaps the response had already been written.  So instead of using the postHandle() method, we tested using the preHandle() method.  Moving the Cookie setting code to the preHandle() method worked!  The Cookie was properly set on all Controller requests regardless of the request being page to page navigation or AJAX calls.

Conclusion

If you want Spring to set a Cookie on ALL requests to Controllers - whether they be page to page requests or AJAX calls, put your code in the preHandle() method of the  HandlerInterceptor interface.




Friday, May 18, 2012

Yahoo! Mail Signatures With Pictures

Introduction

Do you use Yahoo! Mail?  Do you want to create a signature for you emails which includes images?  I wanted to create a signature with an image to go and view my LinkedIn account, but every time I tried to create the signature the image would never appear.  I finally figured out the trick.  Read on.

How To

To include an image in your Yahoo! Mail signature is actually very easy, however you need to do it in the right way otherwise it will not work.  This is what you need to do.
  1. Go to your Yahoo! Mail options and select Signature.
  2. Select "Show a rich text signature" from the dropdown box.
  3. Go to the website which has your image, right click the image on the website, and select "copy image". This is the trick!  You cannot simply copy an image from your desktop.  You must Go to the website which has your image, right click the image on the website, and select "copy image".  If the image you want is not on any website, see below for what to do about that...
  4. Go back to the Signature, put the cursor where you want to have the image, right-click and paste.
That's it.  Again, the trick is you must Go to the website which has your image, right click the image on the website, and select "copy image".  You cannot simply copy an image from your desktop!

Now when you send an email, your signature with the image will be included.  Some email clients will initially block the image, but right-clicking on the email and selecting something like "show images" will reveal it.

Image Not On A Website?

If the image you want on your Yahoo! Mail signature is not on a website, then you need to get it on one. How do you do this?  Easiest thing to do is to use Flickr. Create an account, and upload your image.  After the image is uploaded, click on the image in your photostream to view it.  Above the image select "Actions" then "View all sizes".  On the All Sizes page, click whatever sized image you want to have on your signature (typically will be a small image!), then right click the image and select "copy image".  Then go back to the Signature, put the cursor where you want to have the image, right-click and paste.

Do you have to use Flickr?  No, any photo sharing site should allow you to do the same thing.  You will have to try it out.

That's it, Enjoy!

Wednesday, February 15, 2012

Create Your Own Java URL Handlers

Introduction
Custom URL handlers are becoming more popular as desktop applications are interacting with Internet or Cloud sources.  For example, to subscribe to a iTunes podcast, a website will have a link which looks something like this:

itpc://www.company.com/public/get/podcast.rss

Where "itpc" is a custom protocol handler created by iTunes to handle URLs of this type.  The purpose of this article is to demonstrate how to create your own URL handler in Java.

Problem
Suppose there is need to create the "JTLC" protocol? If I were to create a URL object with this protocol...

public static void main(String[] args) throws Exception
{
  String urlStr 
    = "jtlc://www.mycompany.com/some/path/to/resource";
  URL url = new URL(urlStr);
}

I will get a nasty stack trace from Java...

Exception in thread "main" java.net.MalformedURLException: 
unknown protocol: jtlc
    at java.net.URL.<init>(URL.java:574)
    at java.net.URL.<init>(URL.java:464)
    at java.net.URL.<init>(URL.java:413)
    at jtlc.main.Main.main(Main.java:14)

This is because Java comes with a set of built in handlers to handle well known protocols like HTTP, but your JTLC protocol is unknown to Java so you get a stack trace.  However, before Java throws the stack trace, it attempts to locate a handler for the JTLC protocol.  If you provide the handler, Java will use it to handle JTLC.

Solution

Package

First (and easiest) thing to do is to create the following package in you application to hold the code for your JTLC protocol handler:

sun.net.www.protocol.jtlc

Java will automatically look in the root sun.net.www.protocol package for protocol handlers.  Furthermore, by adding the additional sub-package jtlc (making the full package name you should use sun.net.www.protocol.jtlc), you are telling Java the code in this package handles the JTLC protocol.  When you try to create a new URL object with the "jtlc" protocol, Java will look in the root sun.net.www.protocol package for a sub-package named jtlc to find the code to handle this protocol.

If you do not use this package, then you will have to set special system properties to tell Java what package holds the handlers for you JTLC protocol. 

URLConnection

Second, you need to create a class which extends java.net.URLConnection.

The name of this class can be whatever you want, but it is good convention to name it after the letters in the protocol.  The class for the JTLC protocol may look something like this:

package sun.net.www.protocol.jtlc;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class JtlcConnection extends URLConnection
{
    public JtlcConnection(URL u) {
        super(u);
    }

    @Override
    public void connect() 
    throws IOException {
      throw new UnsupportedOperationException(
        "The connect() method is not supported"
      );
    }
   
    @Override
    public Object getContent() 
    throws IOException {
      throw new UnsupportedOperationException(
        "The getContent() method is not supported"
      );
    }

    @Override
    public InputStream getInputStream() 
    throws IOException {
      throw new UnsupportedOperationException(
        "The getInputStream() method is not supported"
      );
    }
}

Of course you will want to provide a real implementations for these methods to handle connecting to your resource and getting a stream to the content.

URLStreamHandler

Finally, you will need to create a class with extends java.net.URLStreamHandler.

THE NAME OF THIS CLASS MUST BE "Handler".  Java will automatically look for a class named Handler inside the sun.net.www.protocol.jtlc package.  If you name this class something other than Handler, Java will not find it.

The purpose of Handler is to create an instance of URLConnection subclass.  An example may look like this:

package sun.net.www.protocol.jtlc;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

public class Handler extends URLStreamHandler
{
    @Override
    protected URLConnection openConnection(URL u)
    throws IOException
    {
        return new JtlcConnection(u);
    }
}


Summary
Although there are more than one way to create your own Java protocol handlers, this is the quickest and easiest way I've found.

That's it.  Enjoy!

NetBeans and Public-Private SSH Key Access to CVS

Introduction
The purpose of this article is to document how to configure NetBeans to access a CVS repository over SSH using public-private keypairs.

Install "Putty" Applications
Putty is located at http://www.chiark.greenend.org.uk/~sgtatham/putty/.  The primary applications need are pageant.exe and plink.exe.

Run Pageant
Startup pageant.exe. It will be accessible via the system tray.





Right click on the pageant icon in the system tray to add the private key you use to access the CVS server.  Keep in mind that putty and all the putty related applications do not work with keys generated by the linux `ssh-keygen` command.  The private key generated by `ssh-keygen` needs to be opened with the puttygen.exe application and converted into a putty specific private key which can then be used in pageant.exe.

Configure NetBeans
Startup NetBeans.  Go to Team > Checkout.  For the CVS repository configuration, select the radio button to "Use External Shell".  In the "Shell Command" textbox, enter the fully qualified path to plink.exe. Click the "Next >" button and the CVS repository will be access via SSH using your public-private keypair.





























That's it.  Enjoy!