RSS
 

Archive for the ‘Java’ Category

Intellij IDEA + Git performance = KIS

13 Mar

Once upon a time .. something happened to Intellij IDEA.

Every Git operation was taking a very long time and all I had to do is to wait while staring at it. “Compare with Latest Repository Version” was taking ages to complete. Something was definitely wrong and although I blamed IDEA EAP builds initially, that was not the case.

Other people reported it works fine for them so I started looking for what could be wrong with my setup and found the reason – an antivirus, of course. I run Kaspersky Internet Security and so far never had any issue with it. But this time it had to be tweaked a little to stop interfering with my activities:

Once it was configured to exclude and trust anything related to IDEA, Git, Java, Groovy, Gradle and Maven things came back to normal. I’m sorry, IDEA, for thinking it was you.

 
No Comments

Posted in Git, Java, Windows

 

Ubuntu: Installing Apache Portable Runtime (APR) for Tomcat

08 Mar



After reading “Introducing Apache Tomcat 6″ presentation by Mladen Turk I decided to enable Apache Portable Runtime (APR) native library for Tomcat.

It was supposed to be as easy as

sudo ./configure
sudo make
sudo make install

but as you may guess, it was a little bit more than that.



1. Installing Apache APR.

“Most Linux distributions will ship packages for APR” – those of Linode don’t, I had a barebone Ubuntu 10.10 box without even "gcc" and "make", let alone Apache APR. Thanks God, networking was not an issue, unlike last time.

wget http://apache.spd.co.il/apr/apr-1.4.5.tar.gz
tar -xzf apr-1.4.5.tar.gz
rm apr-1.4.5.tar.gz
cd apr-1.4.5/
sudo apt-get install make
sudo ./configure
sudo make
sudo make install



2. Installing Tomcat Native.

wget http://off.co.il/apache//tomcat/tomcat-connectors/native/1.1.20/source/tomcat-native-1.1.20-src.tar.gz
tar -xzf tomcat-native-1.1.20-src.tar.gz
rm tomcat-native-1.1.20-src.tar.gz
cd tomcat-native-1.1.20-src/jni/native
sudo ./configure --with-apr=/usr/local/apr

The result was

checking build system type... x86_64-unknown-linux-gnu
..
checking for APR... yes
..
checking for JDK location (please wait)... checking Try to guess JDK location... configure: error: can't locate a valid JDK location

Ouch! “Can’t locate a valid JDK location” ? On my machine?

$ which java
/home/user/java/jdk/bin/java
$ echo $JAVA_HOME
/home/user/java/jdk
$ java -version
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)

But for some reason "tomcat-native-1.1.20-src/jni/native/configure" script didn’t see my "JAVA_HOME" variable no matter what and even installing "sun-java6-jdk" didn’t help much. After patching the "configure" script to dump locations it was looking for “valid JDK” I had:

..
configure: [/usr/local/1.6.1]
configure: [/usr/local/IBMJava2-1.6.0]
configure: [/usr/local/java1.6.0]
configure: [/usr/local/java-1.6.0]
configure: [/usr/local/jdk1.6.0]
configure: [/usr/local/jdk-1.6.0]
configure: [/usr/local/1.6.0]
configure: [/usr/local/IBMJava2-1.6]
configure: [/usr/local/java1.6]
configure: [/usr/local/java-1.6]
configure: [/usr/local/jdk1.6]
configure: [/usr/local/jdk-1.6]
..

Ok then, here you have it now:

sudo ln -s ~/java/jdk/ /usr/local/jdk-1.6
sudo ./configure --with-apr=/usr/local/apr
sudo make
sudo make install

And with

..
export LD_LIBRARY_PATH='$LD_LIBRARY_PATH:/usr/local/apr/lib'
..

I now had a beautiful log message in "catalina.out":

..
Mar 7, 2011 11:51:02 PM org.apache.catalina.core.AprLifecycleListener init
INFO: Loaded APR based Apache Tomcat Native library 1.1.20.
Mar 7, 2011 11:51:02 PM org.apache.catalina.core.AprLifecycleListener init
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
Mar 7, 2011 11:51:03 PM org.apache.coyote.AbstractProtocolHandler init
..

As soon as "evgeny-goldin.org" moves to its new location on the brand-new Linode box it will benefit from this performance optimization. I’ll describe the migration process and reasons for it a bit later, once it is done.

 
3 Comments

Posted in Java, Linux, Tomcat

 

Watch out, illegal Jetty URL

02 Nov

Is the following URL legal?


http://host?param=value

Well, for Jetty 7.x it is not. And I couldn’t understand why before a friend of mine pointed to a missing "/":


http://host/?param=value

As it appears, if you have a ContextHandler registered to service "/" requests, as we did, Jetty rejects any of them coming without a slash with 400 "Bad request" response.

If you try to use the first URL in a browser, it will add a missing slash automatically. But Apache HttpClient isn’t that smart, unfortunately.

Watch out!

 
No Comments

Posted in Code, Java, Web

 

Neat Delicious trick – keywords combination

26 Aug

The real beauty of Delicious lies in keywords combinations.

I mentioned already that Delicious is my #2 most favorite productivity tool. Within time I developed a set of the most frequently-used keywords in the right column that can be combined with tags in the left column:

"hudson" - Mailing list - "ml"
"spring" - Issue tracker - "jira"
"ant" - API documentation - "api"
"jfrog" - Documentation - "doc"
"groovy" - Maven repository - "repo"
"maven" - Source code browsing - "code"


Now with a "d"-keyworded Delicious search I only need to type "d spring api" to get to

Lots of other useful combinations are also available:

"groovy ml" "hudson ml" "spring ml" "maven ml" "jfrog ml"
"groovy jira" "hudson jira" "spring jira" "gmaven jira" "jfrog jira"
"groovy api" "java api" "spring api" "maven api" "apache api"
"java7 doc" "java doc" "spring doc" "css doc" "html doc"
"groovy repo" "hudson repo" "apache repo" "plugins repo" "jfrog repo"
"groovy code" "hudson code" "spring code" "gmaven code" "groovypp code"


So jumping to a mailing list or API documentation for X is just a matter of "d X ml" or "d X api". Fast!

 

Artifactory REST API with Spring 3 RestTemplate

22 Aug

Artifactory’s REST API is something I was using quite a lot recently and would like to share my experience here. Normally, there’s no need to turn to the REST API when working with Artifactory and in most cases Maven, Hudson or TeamCity plugins are the only ones “talking” to it.

But in this specific case Artifactory was used as a general storage for company’s binaries (a capability I praised a lot in the past): Java agent downloads packed Lucene indexes from Artifactory where they are uploaded to by a Hudson job running Maven and Spring Batch.

It is an interesting and very educating project which I’ll surely explore in more details later. Today, I would like to talk about the REST part of it, the way it is used with a SpringTemplate and some Artifactory-specific nuances, marked with

1. Choosing the client.

Initially I was considering Jersey as a client library knowing JFrog teams uses it to provide Artifactory’s REST support. But then I decided to stick to Spring since version 3 was already used throughout the project and one of its new features is exactly what I needed here – a RestTemplate. Why not give it a try?

2. DAO.

Accessing any external resource is normally done through a DAO layer. Accessing Artifactory shouldn’t be an exception to this rule so we have a DAO interface and an ArtifactoryDAO implementation.

public interface DAO
{
    int  getLatestVersion ( String serviceName, String dataType );
    File download ( File dir, String serviceName, String dataType, int version );
    void deleteOldVersions( String serviceName, String dataType, int oldVersions );
}

serviceName and dataType are our analogies to Maven’s groupId and artifactId. The interface allows a client to get a number of the latest version, download any version locally and delete old versions.

3. Spring time!

Java client gets a DAO implementation injected to it, which in turn gets a RestTemplate injected as well, configured with a custom message converter, more on that below.

<bean ... >
    <constructor-arg name = "dao"  ref = "artifactoryDAO"/>
</bean>

<bean name="artifactoryDAO" class="...">

    ...

    <constructor-arg name  = "rest">
        <bean class="org.springframework.web.client.RestTemplate">
            <property name="messageConverters">
                <list>
                    <bean class="JsonMessageConverter"/>
                </list>
            </property>
        </bean>
    </constructor>
</bean>



4. Working with a RestTemplate.

As one would expect, working with a RestTemplate is pretty straightforward. From all convenient methods available I picked up an exchange() one to provide a general-usage wrapper:

<T> T request( String url, HttpMethod method, Class<T> responseType )
{
    HttpHeaders headers = new HttpHeaders();
    headers.set( "Accept",        "application/json" );
    headers.set( "Authorization", auth());

    T response = rest().exchange( url,
                                  method,
                                  new HttpEntity<String>( headers ),
                                  responseType ).getBody();

    assert (( response != null ) &&
            ( responseType.isAssignableFrom( response.getClass())));

    return response;
}


There is a need to specify a correct "Accept" header since RestTemplate sends "Accept: text/plain" by default which is not accepted by Artifactory.

With built-in HttpMessageConverter and its sub-classes there’s no need to worry about conversion of a REST response, JSON in our case, to an object:

Map<String, Object> m =
   request( "http://artifactory/api/storage/repo/serviceName/dataType",
            HttpMethod.GET,
            Map.class );

Last request() argument defines a type of an object to which response will be converted, considering its "Content-Type" and converters available. I was mostly using a Map when reading Artifactory responses, like “folder info”.


There is a need to extend MappingJacksonHttpMessageConverter in order to recognize "application/vnd.org.jfrog.artifactory.storage.folderinfo+json", returned by Artifactory as JSON:

import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;

public class JsonMessageConverter extends MappingJacksonHttpMessageConverter
{
    @Override
    public boolean canRead ( Class<?> clazz, MediaType mediaType )
    {
        return
            ( super.canRead( clazz, mediaType ) ||
              (( "application".equals( mediaType.getType())) &&
              ( mediaType.getSubtype().startsWith( "vnd.org.jfrog.artifactory" ))));
    }
}



5. Reading InputStream and downloading files.

Message converters work really nice if response is not large and fits into memory. But how about downloading files? I can not assume their content will fit into memory and would like to access a raw InputStream.

It is also possible but in a different way, using an execute() method with two callbacks:

import org.apache.commons.io.IOUtils;

...

private static final RequestCallback ACCEPT_CALLBACK =
    new RequestCallback()
    {
        @Override
        public void doWithRequest ( ClientHttpRequest request ) throws IOException
        {
            request.getHeaders().set( "Accept", "application/json" );
        }
    };

private static class FileResponseExtractor implements ResponseExtractor<Object>
{
    private final File file;
    private       File file () { return this.file; }

    private FileResponseExtractor ( File file )
    {
        this.file = file;
    }

    @Override
    public Object extractData ( ClientHttpResponse response ) throws IOException
    {
        InputStream  is = response.getBody();
        OutputStream os = new BufferedOutputStream( new FileOutputStream( file()));

        IOUtils.copyLarge( is, os );
        IOUtils.closeQuietly( is );
        IOUtils.closeQuietly( os );

        return null;
    }
}

...

rest().execute( downloadUrl,
                HttpMethod.GET,
                ACCEPT_CALLBACK,
                new FileResponseExtractor( new File( downloadDir, fileName )));


Note that to download an artifact with a REST "/api/download/" method requires Artifactory Power Pack license but you can also download an artifact with a regular URL like
"http://artifactory/repo/serviceName/dataType/version/dataType-version.tar".


6. Downloading large files.

I blogged recently about ServletResponse.setContentLength() problem: it doesn’t work with files larger than 2Gb and sends a negative "Content-Length" header due to int overflow.

This problem has also appeared in Artifactory bit it is fixed already, we just need to wait for version "2.2.6" to be released.

Spring 3 uses Apache HttpClient downloading an empty file if negative "Content-Length" header is sent. So I could not download files larger than 2Gb with Spring but I could with wget!

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;

...

String command  = String.format(
    "wget -S -nv -O \"%s\" -T 120 \"%s\" --cache=off %s",
    destinationFile, sourceFileUrl, credentials );

int    exitCode = new DefaultExecutor().execute( CommandLine.parse( command ));

As it appears, "wget" works just fine with a negative "Content-Length" which, btw, is not that unusual and can be sent in those cases when response length is not known in advance.

7. Deleting artifacts.

Artifactory’s API doesn’t really mention it but artifacts or folders can be deleted as well, provided client has enough permissions to do so, of course:

request( "http://artifactory/repo/serviceName/dataType",
         HttpMethod.DELETE,
         Map.class );



8. Security.

HTTP calls can be authenticated by overriding Spring’s CommonsClientHttpRequestFactory and configuring RestTemplate to use it:

public class ArtifactoryCommonsClientHttpRequestFactory
    extends  CommonsClientHttpRequestFactory
{
    private final String user;
    private final String password;

    public ArtifactoryCommonsClientHttpRequestFactory ( String user,
                                                        String password )
    {
        this.user     = user;
        this.password = password;
    }

    @Override
    public HttpClient getHttpClient ()
    {
        HttpClient client = super.getHttpClient();

        if ( this.user != null )
        {
            client.getState().setCredentials(
                AuthScope.ANY,
                new UsernamePasswordCredentials( this.user, this.password ));
        }

        return client;
    }
}
<bean class="org.springframework.web.client.RestTemplate">
    <property name  = "requestFactory">
        <bean class = "ArtifactoryCommonsClientHttpRequestFactory">
            <constructor-arg name="user"     value="..."/>
            <constructor-arg name="password" value="..."/>
        </bean>
    </property>

    ...
</bean>


Artifactory encrypted password can be specified as a "password".

But this approach didn’t work well in all scenarios, particularly in DELETE calls which went out unauthorized so I switched to setting an "Authorization" HTTP header, as shown in “Working with a RestTemplate” section above.

9. Related links.


10. Summary.

Overall, it took some time to implement ArtifactoryDAO and sort out all issues involved: Spring converters, request and response headers, authentication, downloading large files, etc .. But it now works extremely well, response time of each REST invocation is normally below 10 ms and Artifactory Hudson plugin eats 2.5Gb files for breakfast!




 

Ant FTP task: adding progress indicator and timeout

18 Aug

Ant FTP task provides no option to display how much the download has progressed so far, similarly to wget or curl verbose options. Setting connection timeout isn’t possible either.

But I had to implement it one day and here is what I have found out:

The basic idea is therefore to redefine an FTP task with improved version of FTPClient making use of CopyStreamListener when calling Util.copyStream(). Let’s do it!

1. New FTP task:

The biggest problem in the whole story is the following line of code: ftp = new FTPClient(). I wish there were a simple IoC-like way to “inject” an FTPClient instance here but Ant does not provide too many ways to customize its default configuration, something like RestTemplate.setRequestFactory(). Alas!

I really wish more people would embrace Spring/IoC way of thinking: everything is a Strategy and therefore can be “injected”, extended, modified, and customized.

So my first attempt was to extend Ant’s org.apache.tools.ant.taskdefs.optional.net.FTP class, override execute() method, copy its original code and replace the assignment. As much as I have no taste for copying someone else’s code in order to modify a line or two, sometimes this is the only way to go. But it didn’t work! execute() method accesses lots of private members, not accessible from a sub-class.

The next option was to copy an entire FTP class and then replace the assignment. If anyone can suggest a better way to do the same I’m all ears. So I ended up with:

public class FTP extends Task
{
    ...
    public void execute() throws BuildException {
        ...
        org.apache.commons.net.ftp.FTPClient ftp = null;
        ...
        ftp = ( verbose ? new FTPClient( getProject()) :
                          new org.apache.commons.net.ftp.FTPClient());
        ftp.setDataTimeout( 5 * 60 * 1000 ); // 5 minutes
        ...
    }
}

Line 08 sets a customized version of FTPClient or a default one, line 10 sets up FTPClient timeout.

2. New FTPClient:

public class FTPClient extends org.apache.commons.net.ftp.FTPClient
{
    private final Project project; // org.apache.tools.ant.Project

    ...

    @Override
    public boolean retrieveFile(String remote, OutputStream local)
    throws IOException
    {
        ...
        Util.copyStream( input,
                         local,
                         getBufferSize(),
                         CopyStreamEvent.UNKNOWN_STREAM_SIZE,
                         new CopyStreamListener( this.project ),
                         false );
        ...
    }
}

Again, I had to copy the original code and “patch” Util.copyStream() call to use CopyStreamListener implementation. Also, I had to copy and slightly modify an FTPConfigurator class to make it accept my new FTPClient.

3. CopyStreamListener:

public class CopyStreamListener implements org.apache.commons.net.io.CopyStreamListener
{
    private final Project project;
    private       long    mbTransferred = 0;

    ...

    @Override
    public void bytesTransferred ( long totalBytes, int bytes, long streamSize )
    {
        long mb = ( totalBytes / ( 1024 * 1024 ));
        if ( mb > mbTransferred )
        {
            mbTransferred = mb;
            this.project.log( "[" + new Date() + "]: [" + mbTransferred + "] Mb transferred" );
        }
    }
}

4. Plugging in new definition of FTP task with ClfAntBuilder:

public class ClfAntBuilder extends AntBuilder
{
    public ClfAntBuilder ()
    {
        super();
        getProject().addTaskDefinition( "ftp", FTP.class );
    }
}

5. Running it:

new ClfAntBuilder().ftp( action         : 'get',
                         server         : host,
                         userid         : username,
                         ... )
{
    fileset( dir       : '...',
             includes  : '...',
             excludes  : '...' )
}

6. The final result:

Trying to override old definition of task ftp
      [ftp] getting files
      [ftp] transferring SomeFile.xml.zip to C:\Temp\SomeFile.xml.zip
[Tue Aug 17 20:43:07 IDT 2010]: [1] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [2] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [3] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [4] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [5] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [6] Mb transferred
[Tue Aug 17 20:43:07 IDT 2010]: [7] Mb transferred
...

Nice!

 
 

Groovy 1.7.3: AnnoMojo + GroovyMojo!

27 Jun



I waited a lot. Oh boy, I did.

 

.. Rewind back half a year ..

 

Writing Maven plugins, or MOJOs, is a real need when you work with Maven. It just feels unnatural not to.

Default Maven behavior and set of plugins is only good for common situations and the moment you’re doing something unusual, like running Spring Batch jobs or generating Hudson jobs in your POM, Maven doesn’t really deliver. In the last year, I had to develop about 10 Maven plugins, now waiting for a final Thomson Reuters approval to be open-sourced (stay tuned!)

Initially, I used Java but later switched to Groovy, for an obvious reasons. With Gmaven plugin it is very easy to develop Groovy Mojos. Good!

The only problem I had after switching to Groovy is lack of Anno Mojo support providing Java 5 annotations to MOJO developer.

Java:

@MojoGoal("doIt")
@MojoPhase("package")
public class MyMojo extends AbstractMojo
{
    @MojoComponent
    private ArtifactFactory artifactFactory;

    @MojoParameter(expression = "${project}", required = true)
    private MavenProject project;

    @MojoParameter (required = false)
    private String runIf;
    ...
}

Groovy:

@MojoGoal("doIt")
@MojoPhase("package")
class MyMojo extends GroovyMojo
{
    @MojoComponent
    def ArtifactFactory artifactFactory;

    @MojoParameter(expression = "${project}", required = true)
    def MavenProject project;

    @MojoParameter (required = false)
    def String runIf;
    ...
}

Groovy version didn’t work :(
Meet “GROOVY-4118″ – “JavaStubGenerator doesn’t generate annotations available in Groovy code”

 

.. Fast forward to Groovy 1.7.3 release ..

 

Shortly before Groovy 1.7.3 release the issue above was addressed and fixed by Paul King and Jochen Theodorou. After some more attempts I made it work and it felt really-really good to get back annotations in Groovy Mojos!

@MojoGoal("doIt")
@MojoPhase("package")
class MyMojo extends GroovyMojo
{
    @MojoComponent
    public ArtifactFactory artifactFactory;

    @MojoParameter(expression = "${project}", required = true)
    public MavenProject project;

    @MojoParameter (required = false)
    public String runIf;
    ...
}

It is required to use:

  • public modifiers
  • Groovy fields

This will not work:

class MyMojo extends GroovyMojo
{
    @MojoParameter (required = false)
    def String runIf;

    @MojoParameter (required = false)
    String something;
}

 

"pom.xml":

<dependencies>
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- To use Anno Mojo annotations in Groovy code -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <dependency>
        <!-- http://www.jfrog.org/artifactory/plugins-releases/org/jfrog/maven/annomojo/maven-plugin-anno/ -->
        <groupid>org.jfrog.maven.annomojo</groupid>
        <artifactid>maven-plugin-anno</artifactid>
        <version>1.3.3</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupid>org.codehaus.groovy</groupid>
        <artifactid>groovy-all</artifactid>
        <version>1.7.3</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <!-- To generate Java stubs and compile Groovy classes -->
        <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <plugin>
            <groupid>org.codehaus.gmaven</groupid>
            <artifactid>gmaven-plugin</artifactid>
            <version>1.2</version>
            <executions>
                <execution>
                    <id>generate-stubs</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>generateStubs</goal>
                    </goals>
                </execution>
                <execution>
                    <id>compile-groovy</id>
                    <phase>process-sources</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <providerselection>1.7</providerselection>
                <verbose>true</verbose>
                <sources>
                    <fileset>
                        <directory>${projects.basedir}/src/main/scripts</directory>
                        <includes>
                            <include>**/*.groovy</include>
                        </includes>
                    </fileset>
                </sources>
            </configuration>
            <dependencies>
                <dependency>
                    <groupid>org.codehaus.gmaven.runtime</groupid>
                    <artifactid>gmaven-runtime-1.7</artifactid>
                    <version>1.2</version>
                    <exclusions>
                        <exclusion>
                            <groupid>org.codehaus.groovy</groupid>
                            <artifactid>groovy-all</artifactid>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupid>org.codehaus.groovy</groupid>
                    <artifactid>groovy-all</artifactid>
                    <version>1.7.3</version>
                </dependency>
            </dependencies>
        </plugin>

        <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <!-- To "activate" Anno Mojo during plugin metadata generation -->
        <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <plugin>
            <groupid>org.apache.maven.plugins</groupid>
            <artifactid>maven-plugin-plugin</artifactid>
            <version>2.5.1</version>
            <dependencies>
                <dependency>
                  <groupid>org.apache.maven.plugin-tools</groupid>
                  <artifactid>maven-plugin-tools-api</artifactid>
                  <version>2.5.1</version>
                </dependency>
                <dependency>
                    <!-- http://www.jfrog.org/artifactory/plugins-releases/org/jfrog/maven/annomojo/maven-plugin-tools-anno/ -->
                    <groupid>org.jfrog.maven.annomojo</groupid>
                    <artifactid>maven-plugin-tools-anno</artifactid>
                    <version>1.3.3</version>
                    <scope>runtime</scope>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

Also, I use the following base class for all Groovy Mojos with commonly-used @MojoParameters:

/**
 * Base GroovyMojo class
 */
abstract class ClfGroovyMojo extends GroovyMojo
{
    private static final Log LOG = new SystemStreamLog()
    public  static       Log getLog () { LOG }

    @MojoParameter ( expression = '${project}' )
    public MavenProject mavenProject
    public MavenProject mavenProject() { this.@mavenProject }

    @MojoParameter ( expression = '${session}' )
    public MavenSession mavenSession
    public MavenSession mavenSession() { this.@mavenSession }

    @MojoParameter ( expression = '${project.build.directory}' )
    public File buildDirectory
    public File buildDirectory() { this.@buildDirectory }

    @MojoParameter ( expression = '${project.build.outputDirectory}' )
    public File outputDirectory
    public File outputDirectory() { this.@outputDirectory }

    @MojoParameter ( expression = '${project.basedir}' )
    public File basedir
    public File basedir() { this.@basedir }

    @MojoParameter ( defaultValue = '${localRepository}' )
    public ArtifactRepository localRepository
    public ArtifactRepository localRepository() { this.@localRepository }

    @MojoComponent
    public PluginManager pluginManager
    public PluginManager pluginManager() { this.@pluginManager }
}

Let me know if something doesn’t work for you. I may provide a complete example of Groovy Mojo.

 
 

Artifactory Online – the case of distributing Groovy++

05 May

After working with open source Artifactory version and thoroughly exploring it’s add-ons I knew it would come a moment to put my hands on its cloud solution – Artifactory Online. It just made sense to “close the loop” this way .. Тhe moment I’ve heard about online instance, running 24×7 without having to take care of anything – it sounded really, really nice.

I can’t say we spend a lot of time administering our open source version in Thomson Reuters. Quite the opposite – I only need to take it down for upgrades from time to time. But it still takes us a machine. A virtual one, of course but still – that’s CPU and memory that could be well spent somewhere else. So having online instance not only gives a peace of mind freeing everybody from taking care of one more server and one more database – it frees some hardware resources as well.

Well, a beauty of cloud computing, when it works. And Artifactory certainly does!

But my first use of Artifactory Online was for slightly different purpose – Maven support for the Groovy++ project. There was a clear need to host Groovy++ binaries in a public Maven repo.

What options are available today?

  • OSS repository hosting from Sonatype.
    It’s a good free solution but like any other free solution it only provides you so much: if you don’t mind being at mercy of other people with certain demands about how your POMs should look like – then it’s a good way to go. But I’d prefer my personal repository, where I can configure it the way I want without sharing it with other projects and asking favors. Also, bear in mind you get no security whatsoever – all your binaries would be open to everybody, anytime. And it only works for open-source projects which can be another showstopper.
  •  

  • Another option is to host a public Apache or nginx server and just make the files available following Maven’s naming conventions, like it’s done on "repo1". Not to mention the lack of security (again) – this kind of storage can be fragile to files corruptions: after all it’s just a dumb files storage, not an intelligent repo manager. You can’t use Maven to deploy artifacts and it provides no additional services, like virtual repos, artifacts searching or usage statistics.
  •  

  • Public hosting of open-source Nexus or Artifactory – it’s much better and we can finally protect it the way we want. But we still need to pay for hosting, memory usage, bandwidth usage and we now need to install and administer it on top of everything else. And put some extra protection, may be.
  •  

  • Artifactory Online. The best way to go, if you ask me. Not only it provides a cloud-based 24×7 running Artifactory instance, but it does so with all add-ons installed, so you really get it the ‘Full Monty’! It solves our original problem, to host binaries in a public Maven repo, but it doesn’t stop there, as I’ll show shortly – running a private Artifactory instance brings other advantages to your projects.

We’ve settled with last option, meet http://groovypp.artifactoryonline.com/!
The initial setup went very fast as there were very few things to take care of, actually:

  1. Registration
  2.  

  3. Creating deployment user and "settings.xml". The fact that Artifactory provides a way to generate new “settings.xml” (skip to 00:10:40) and store an encrypted passwords (00:11:55) comes in very handy:

    <servers>
        <server>
            <id>groovypp</id>
            <username>username</username>
            <password>\{ABAeqq\}pIcMooZ8G/2Y2drgC99SDw==</password> <!-- Sample -->
        </server>
    </servers>
    
  4. Instructing Maven about new repositories:

    <properties>
        <repo>http://groovypp.artifactoryonline.com/groovypp</repo>
    </properties>
    
    <distributionmanagement>
        <repository>
            <id>libs-releases-local</id>
            <url>${repo}/libs-releases-local</url>
        </repository>
    </distributionmanagement>
    
    <repositories>
        <repository>
            <id>libs-releases</id>
            <url>${repo}/libs-releases</url>
        </repository>
    </repositories>
    
    <pluginrepositories>
        <pluginrepository>
            <id>plugins-releases</id>
            <url>${repo}/plugins-releases</url>
        </pluginrepository>
    </pluginrepositories>
    

As you see, we’re using new repo not only for <distributionManagement> but as our only Maven repository.
From now on we only talk to groovypp.artifactoryonline.com/groovypp/libs-releases.
This is “virtual repository”, a “gateway” Maven will connect to for retrieving any 3-rd party library. I can now add additional Maven repositories by editing it in Artifactory, there’s no need to update the POM any more when new external repos are added to the project.

After this quick setup I ran it for the first time. I was expecting somewhat slower performance than the one we have in the office where Artifactory is running on the same network. After all, we’re talking here about remote repository running somewhere across the ocean:

But the download was pretty fast. It depends on the bandwidth, of course but I can’t say that significantly more distant repository has slowed me down. UI was very response and Maven’s filling of empty local repo was fast enough as not to notice any significant difference. Good!

Groovy++ project is now happily using Artifactory Online for several months and releases, you’re always welcome to download the latest version manually or give it a try with Maven.

What else can I say about running a private repo like that?

I think the main beauty of it is being able to “go public” in matter of minutes. No setups, no worries – you have your very own binaries storage, intelligent and secured that can be used for any purpose. That’s right, Artifactory can serve any binaries, not necessarily Maven’s artifacts. So one can store there practically anything and then secure or backup it safely.

Makes me think of various App Store services where people publish their Android / iPhone applications and enjoy the ride. That’s good, I believe “going public” should be easy for anyone today – this way creativity meets no entry barriers!

I only have a single request to Artifactory developers – an option to create aliases to existing repo. This would allow to reuse the same repository for different projects or purposes: http://projectA.artifactoryonline.com/ and http://projectB.artifactoryonline.com/ will point to the same Artifactory instance but will be used by different people.

Overall, a very pleasant experience!
Exactly what I was expecting – can’t help it but these guys never disappoint :)

 
 

Uploading files – multipart HTTP POST and Apache HttpClient

01 May

I had to implement a files transferring mechanism where one machine was sending files to another using "multipart/form-data" POST request. It can be done using Apache’s Commons FileUpload and HttpClient.

The receiving part was an easy one:

<dependency>
    <groupid>commons-fileupload</groupid>
    <artifactid>commons-fileupload</artifactid>
    <version>1.2.1</version>
</dependency>

We parse an incoming request with ServletFileUpload and get a list of FileItems in return. Each FileItem is either form’s input field or a file uploaded:

if ( ServletFileUpload.isMultipartContent( request ))
{
    List<fileitem> fileItems =
        new ServletFileUpload( new DiskFileItemFactory( 1024 * 1024, DIR )).
        parseRequest( request );

    for ( FileItem item : fileItems )
    {
        String fieldName = item.getFieldName();

        if ( item.isFormField()) { item.getString()      } // Form's input field
        else                     { item.getInputStream() } // File uploaded
    }
}

In our case, we use DiskFileItemFactory to store files larger than 1Mb in a temporary DIR. After reading file’s InputStream and storing the data in a proper storage – we need to delete the temporary copy: item.delete().

It’s the sending part that came out to be a bit trickier. Initially, I was using a simple HTML form:

<form action="http://localhost" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="text" name="paramName">
    <input type="submit" name="Submit" value="Upload File">
</form>

But then I’ve switched back to Java and assumed HttpClient will do the job.

<dependency>
    <groupid>org.apache.httpcomponents</groupid>
    <artifactid>httpclient</artifactid>
    <version>4.0.1</version>
</dependency>

Eventually, it did but it took me some time to figure out how. The problem with HttpClient is that it provides a nice tutorial and various usage examples but none of them actually mentions a word about uploading files!

I’ve figured out I need to set an instance of HttpEntity to request but it seemed like it’s going to be either a StringEntity or FileEntity but not both. How come ?! Why is it so hard to send a usual POST request with String and file parameters?

Ok, it’s Google time.

Some examples and documentation were referring to an outdated version when HttpClient was part of Apache Commons and, therefore, were of no use for me – the API has changed dramatically. Until I’ve found this example that finally saved my day. Radomir, thank you!

The solution is to use an additional Apache component – HttpMime:

<dependency>
    <groupid>org.apache.httpcomponents</groupid>
    <artifactid>httpclient</artifactid>
    <version>4.0.1</version>
</dependency>

<dependency>
    <groupid>org.apache.httpcomponents</groupid>
    <artifactid>httpmime</artifactid>
    <version>4.0.1</version>
</dependency>

and then we finally get to use a magical MultipartEntity:

HttpClient client = new DefaultHttpClient();
client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

HttpPost        post   = new HttpPost( url );
MultipartEntity entity = new MultipartEntity( HttpMultipartMode.BROWSER_COMPATIBLE );

// For File parameters
entity.addPart( paramName, new FileBody((( File ) paramValue ), "application/zip" ));

// For usual String parameters
entity.addPart( paramName, new StringBody( paramValue.toString(), "text/plain",
                                           Charset.forName( "UTF-8" )));

post.setEntity( entity );

// Here we go!
String response = EntityUtils.toString( client.execute( post ).getEntity(), "UTF-8" );

client.getConnectionManager().shutdown();

Note the use of EntityUtils for reading the response.

That’s it.

I only wish library authors were providing better support and examples for more common cases like files uploading in our case. I mean, come on, when people get to use HttpClient they either want to send a usual request or upload a file, same thing they do with browser. Am I wrong here?

 
15 Comments

Posted in Java, Web

 

Searching for classes inside *.jar files

13 Jan

Just a quick tip about searching for classes inside *.jar files.
TC rocks! I was introduced Agent Ransack today but .. nothing beats good old Alt+F7 :)

 

1

 
No Comments

Posted in Java, PC