RSS
 

Archive for the ‘Artifactory’ Category

Maven Plugins v0.2.1 – re-written and open-sourced!

05 Mar

A "0.1" release of these Maven plugins back in November brought a lot of attention to the project, which showed me that other Maven developers find them as useful as I do. Later, people started to send me new suggestions and open YouTrack issues which I had an extreme pleasure to work on.

Version "0.2.1" was recently released and I believe this version is of paramount importance and it is much more than just an upgrade. Lots of things have changed for the project in addition to features added and bugs fixed:

  • Lots of people have helped me to work on this version by talking to me personally, over e-mail or by opening issues. A big thank you to all of you! Without your impact, ideas and bug reports this release would contain much less.
  • The code was re-written in Groovy and open-sourced. Writing Maven plugins in Groovy is such a big fun, I’ll be talking about it in “Groovy Builds” session at Gr8Conf this year. Come to see how Maven can be better!
  • A separate testing project was created with hundreds of thorough tests and examples. A GCommons library was extracted from the code into a standalone project.
  • Documentation was updated and GroovyDoc is now available as well. But if you’re short of time here’s a quick presentation. The project now has its own mailing list and you’re welcome to join in order to stay informed and take part in features discussions and prioritizing future progress.
  • The project gets more exposure: the Spring issue of Methods & Tools will come out with my article describing the plugins’ purpose and advantages for less technical people, and GroovyMag March 2011 just came out with an article about GCommons. And if all goes well, the hudson/jenkins plugin will be covered by “Jenkins: The Definitive Guide” as John Ferguson Smart kindly accepted my proposal to contribute to the book. This sounds really nice and I hope this is just the beginning.
  • JFrog and JetBrains tools keep working so well that I’m now convinced that a combination of Artifactory, TeamCity and YouTrack is an excellent option these days.


Now, what actually changed in this release? Versions "0.2" and "0.2.1" release notes are available in YouTrack and on the Wiki but here are some highlights:

  • All plugins now support a <runIf> conditional execution. It allows to invoke them conditionally, something that is otherwise impossible with Maven.
  • "maven-copy-plugin" – bullet-proof FTP download, Zip entries unpack, archives update, <runIf> per <resource>, FTP/SCP uploads.
  • "maven-hudson-plugin" – free-style jobs, CVS support, <properties>, <authToken>.
  • "maven-assert-plugin"<assertGroovy> with File.directorySize(), very handy in tests!
  • "maven-sshexec-plugin" – multiple commands support, key-based authentication, echo of the current directory and commands executed.


Now, what’s next? A lot. I also plan to polish GroovyDocs as much as possible.

 

New Maven plugins released!

14 Nov



Note, an update is available.

After a lot of work I released version "0.1" of the following Maven plugins:

  • "maven-copy-plugin" is an alternative to Maven plugins like assembly, resources, dependency, and truezip. Its purpose is to make working with archives and dependencies very easy.



    It is a Swiss Army knife if you need to copy, pack and unpack files, archives and Maven dependencies. Content replacement, network support, Groovy extension points, attaching archives created as Maven artifacts – it is possible to perform all operations in a single Maven plugin! Oh boy, just give it a try.

  • "maven-hudson-plugin" allows to generate Hudson jobs, keeping any amount of them in a single POM. Jobs can be organized hierarchically with inheritance and can invoke each other, Artifactory deployment is supported as well. Managing tens or hundreds of Hudson jobs becomes possible when they are kept in one place and inherit each other with a sensible defaults.
  • "maven-spring-batch-plugin" allows to invoke Spring Batch jobs as part of Maven build.
  • "maven-mail-plugin" allows to send mails with attachment from Maven.
  • "maven-sshexec-plugin" allows to execute commands on a remote server over ssh.
  • "maven-properties-plugin" allows to create Maven properties dynamically with Groovy snippets.
  • "maven-timestamp-plugin" allows to create a timestamp Maven property.
  • "maven-assert-plugin" allows to verify various build assertions: properties are defined, files exist and files/directories content is identical.
  • "maven-find-plugin" allows to set a Maven property to location of folder dynamically found for each module built. It helps in situations where build needs to access files in other locations than the current module.
 

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!




 

JFrog: To Build or Not to Be

05 Aug

JFrog’s “To Build or Not to Be” seminar was an exceptional one. Usually, there are very few events fully devoted to the subject of builds and build tools. Lucky us we had this one with so many key people of today’s build arena:

Two of the sessions are now available online in JFrog blog, a big number of photos are available in Picasa album of Guy Nir.

I allowed myself to ask participants some questions.

Sergey Anchipolevsky, Max Feldman – TeamCity:

“What does "Git (JetBrains)" Git integration of TeamCity mean?”

It’s just a version of Git plugin developed by JetBrains, to differentiate it from previous Git plugins.

“Is it possible to "tag" a YouTrack issue?”

That’s a YouTrack question but yes, you can tag an issue.

“Is it possible to backup YouTrack issues?”

Sure, you can schedule YouTrack database backups with a cron expression or run it manually.

“Is it possible to re-order YouTrack issues by drag-n-dropping them, similarly to how it is done in Nozbe?”

No, it can’t be done. YouTrack isn’t really a task management tool, like Nozbe. It is an issue tracker which is something different. The order between issues is specified by a "sort by" criteria so they can’t be arbitrarily re-ordered with drag-n-drop. But you should probably take a look at checkvist.com, a simpler task management tool developed by JetBrains developers.

“Ok, TeamCity question. What do you think of cloud-based TeamCity version, similar to Artifactory Online. Can it be done?”

Oh, that’s a good one. First of all, let’s get something clear. You can’t provide a SaaS solution for a build server. Normally, SaaS solutions, like Artifactory Online that you have mentioned, are based on a shared resources, hosted by provider. But something like a build server is too resource-hungry and can’t be shared with other users.

My comment: I agree. Experience shows how unshareable build servers are. You really want a dedicated machine for each one of them, trying to run too many builds on the same machine may quickly bring the server to its knees and overall response time drops dramatically.

But something else can be done. First of all, you can run build agents on EC2 today. Second, we can provide TeamCity images ready to be run on EC2 as well. But it’s always better if you run main TeamCity server inside your organization, on your own IT infrastructures. We haven’s seen too many customers willing to out-source the build server completely: networking issues, server configurations, additional setups, security .. it’s too much of a pain and, again, can hardly be shared between different users.

“I see, thank you. Is it possible to develop plugins for TeamCity?”

Sure!

Yoav Landman – Artifactory:

“After hearing your session about module development options in Java – what do you think of JPF? Is it somehow a player in today’s Java module-space?”

I looked at JPF several times before as an application level module system, but I don’t think it amounts to a Java language module system. You can incorporate it into an application but you can’t build or run a full application based strictly on JPF modules (last time I checked).

“You mentioned Java moduling system is not united and fragmented, we have lot’s of approaches and various repos available. Can “repo1″ be *the one*?”

It kind of is at the moment and there are no much alternatives available. I wish it would be more developer friendly, though.

“As people are running more and more Artifactory Online instances, does it contribute to overall module-space fragmentation? Don’t get me wrong, I love it and had a great success in providing a Maven support for the Groovy++ project with a help of groovypp.artifactoryonline.com. I have my own repo set up at evgeny-goldin.org/artifactory that I’m about to use for my personal projects. So it’s cool, by all means. Still, the fragmentation issue you have just mentioned during the session just made me wonder .. “

Having a central repository and a central URL to get things from is a great thing. However, Maven central brings some long-lasting issues with it: the existence of module developers is not obvious and artifacts cannot be linked back to them; publishing is not straightforward – rather than getting an account and a simple Web UI/REST deploy, you need to set up rsync or learn a commercial tool to do that – in both cases the final published artifact looses relation to its creator account; repositories provide no REST API, there’s no access to statistics, and mostly – no searches: rather than sending a query to Central you have to download index file and search on the client side. I think these are the main concerns that keep people managing their public repositories on apache/sf/google-code/svn/artifactory/nexus etc.

Hans Dockter – Gradle:

“Gradle is a truly awesome build tool. Are you planning to provide a way to invoke Maven plugins from Gradle?”

Yes, it is of very high priority, we will provide a way to “import” and run an existing "pom.xml"

“In fact, I was thinking more of invoking a plugin directly, given its <groupid>, <artifactid>, and <version>. Similarly to how it is done by Mojo Executor .. “

Ah, right, right, this one. Sure, it should be done as well, although it is not simple, you know. To invoke a Maven plugin you need to “bind” it with a Maven container but we may probably trick into thinking there’s a Maven container while it will be talking to a Gradle run-time environment. Anyway, it’s definitely on our TODO list.

“The reason I ask is to ease Maven to Gradle migration or simply re-use an existing Maven plugins, some of which are really good! Now, I believe Gradle is a great tool but it requires a serious understanding of what you’re doing. In some aspects, it reminds me of Git where some people consider it to be too complex sometimes”

Yes, there’s a need to provide a really simple guidance for a people to switch between the tools or adopt a new one, like Gradle. But you need to understand your tool, there’s no doubt about it.

It was time to say good bye. But I hope to see you all again!
A big thanks to Guy Nir and Shlomi Ben Haim for generously allowing me to use some of the photos they have made.

 

Hudson and Artifactory Maven repository

17 Jul

For a long time I was sure there’s no Hudson Maven repository.

I was wrong:

<dependency>
    <groupid>org.jvnet.hudson.main</groupid>
    <artifactid>hudson-war</artifactid>
    <version>1.373</version>
    <type>war</type>
</dependency>
<dependency>
    <groupid>org.jvnet.hudson.plugins</groupid>
    <artifactid>greenballs</artifactid>
    <version>1.6</version>
    <type>hpi</type>
</dependency>
<dependency>
    <groupid>org.jvnet.hudson.plugins</groupid>
    <artifactid>artifactory</artifactid>
    <version>1.2.0</version>
    <type>hpi</type>
</dependency>

Similarly, Artifactory:

<dependency>
    <groupid>org.artifactory</groupid>
    <artifactid>artifactory-web-war</artifactid>
    <version>2.2.5</version>
    <type>war</type>
</dependency>

And Maven itself:

<dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>apache-maven</artifactId>
    <version>2.2.1</version>
    <classifier>bin</classifier>
    <type>zip</type>
</dependency>

Why would one want to get Hudson, Artifactory and Maven distribution through Maven?
In our case, we create a Tomcat setup with Hudson, Artifactory and Maven already installed and configured, I’ll write about this project later.

 

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 :)

 
 

Using Groovy++ with Maven

18 Mar

Note, an update is available.

Groovy++ artifacts are now deployed in Maven repository at:

http://groovypp.artifactoryonline.com/groovypp/libs-releases-local/
and
http://groovypp.artifactoryonline.com/groovypp/libs-snapshots-local/

 

So you can compile your Groovy++ sources with Maven.
As in Groovy, where you pick up either groovy.jar or groovy-all.jar – you can do the same here:

  • Use groovy + groovypp + asm + antlr + commons-cli:

    <dependency>
        <groupid>org.codehaus.groovy</groupid>
        <artifactid>groovy</artifactid>
        <version>1.8.0-beta-1-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupid>org.codehaus.groovy</groupid>
        <artifactid>groovypp</artifactid>
        <version>0.1.18</version>
    </dependency>
    <dependency>
        <groupid>asm</groupid>
        <artifactid>asm-all</artifactid>
        <version>3.2</version>
    </dependency>
    <dependency>
        <groupid>antlr</groupid>
        <artifactid>antlr</artifactid>
        <version>2.7.7</version>
    </dependency>
    <dependency>
        <groupid>commons-cli</groupid>
        <artifactid>commons-cli</artifactid>
        <version>1.2</version>
    </dependency>
    

  • Use groovypp-all:
    <dependency>
        <groupid>org.codehaus.groovy</groupid>
        <artifactid>groovypp-all</artifactid>
        <version>0.1.18</version>
    </dependency>
    

As you see, the second way is much simpler so I suggest you stick to “groovypp-all”. It’s the same as a longer version but with all required libraries packaged nicely in one bigger jar. 

Of course, you’ll need to instruct Maven about repository:

    <properties>
        <repo>http://groovypp.artifactoryonline.com/groovypp</repo>
    </properties>

    <repositories>
        <repository>
            <id>libs-releases</id>
            <url>${repo}/libs-releases</url>
        </repository>
        <repository>
            <id>libs-snapshots</id>
            <url>${repo}/libs-snapshots</url>
        </repository>
    </repositories>

    <pluginrepositories>
        <pluginrepository>
            <id>plugins-releases</id>
            <url>${repo}/plugins-releases</url>
        </pluginrepository>
        <pluginrepository>
            <id>plugins-snapshots</id>
            <url>${repo}/plugins-snapshots</url>
        </pluginrepository>
    </pluginrepositories>

.. and configure GMaven plugin:

            <plugin>
                <groupid>org.codehaus.gmaven</groupid>
                <artifactid>gmaven-plugin</artifactid>
                <version>1.2</version>
                <executions>
                    <execution>
                        <id>compile-groovy</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <providerselection>1.7</providerselection>
                            <verbose>true</verbose>
                            <debug>true</debug>
                            <stacktrace>true</stacktrace>
                            <sources>
                                <fileset>
                                    <directory>${project.basedir}/src</directory>
                                    <includes>
                                        <include>**/*.groovy</include>
                                    </includes>
                                </fileset>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
                <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>groovypp-all</artifactid>
                        <version>0.1.18</version>
                    </dependency>
                </dependencies>
            </plugin>

Full examples:

If you didn’t do so yet – you’re welcome to join a community to take part in Groovy++ discussions and be notified about new releases (@groovypp is available as well).

Also, you can follow Alex Tkachman who is Groovy++ inventor at @alextkachman and DZone.

 

Artifactory Power Pack – is it really powerful ?

08 Mar

We have been using Artifactory in Thomson Reuters (ClearForest) for more than a year now.

My first Maven repository manager was Nexus – we were using it in my previous workplace. When I came to Thomson Reuters and started working on new CM infrastructures – I decided to switch to Artifactory, though. Mostly due to its richness of features and for supporting an efficient checksum-based storage model for binaries, which was the biggest difference for me between the two products.

We started with version 1.3 and then went through all major upgrades: 2.0, 2.1 (it was a big update: new searches, artifacts metadata, add-ons, and move/copy operations on artifacts). We’re now running version 2.2.1 with Add-ons Power Pack.

To tell you the truth – we really love it as it worked perfectly through the whole period (except in some cases where support and solutions were provided on the same day!).

Within time, I’ve also learned to appreciate how Artifactory goes away from being Maven-only repository manager and becomes a general-purpose storage manager for any kind of binaries and build system. Ivy and Gradle support (with new collaborative relationship just announced) is already available and it’s a really good start. Integration with Hudson and TeamCity (will be available soon) also comes very handy. In fact, one can store any kind of binaries in Artifactory, not related to either Maven or Java in any way!

In short, Artifactory knows (and willing) to cooperate with all major players on today’s arena and that’s a really impressive achievement for something that started as a free-time Maven repo manager project. Well done, guys!

But my particular interest in the last months was it’s Power Pack offering.
We run Hudson pretty intensively as our CI server so when JFrog-ers announced they have a special “Hudson support” – they certainly had my attention!

Now as we have it installed I’d like to see .. Does it matter? I mean, is it really that powerful? The short answer is yes, it does and yes, it is. There’s no doubt about it – see below.

To start with – Power Packs offers different things and even with all our appreciation to Artifactory we’re not using all of them. There’s simply no need for us to right now.
But what we do use is really saving us time (and, therefore, money) on a daily basis:

Now, let’s take each of them apart.

 

Hudson integration

This one is definitely the best and draws the most attention, for obvious reasons. The idea is both simple and ingenius – let’s ask CI server (Hudson/TeamCity/Bamboo) to push all build environment data to Artifactory! After all, when a build job is running – it has all environmental information one can think of : OS type and version, JVM version, modules built, their dependencies and versions … Until today all this information was buried somewhere
in Hudson logs and deleted, eventually (I mean, we do need to clean up our build logs sometimes, don’t we?)

Not any more – Hudson integration establishes a bi-directional link between Artifactory and Hudson for each job run. Finally, those two start talking to each other!

How it works:

  • Hudson Artifactory plugin is installed
  • Hudson is configured and Artifactory server is added
  • Hudson job is configured to run "mvn clean deploy install" 
       That’s right, we’re not using maven-deploy-plugin any more
  • Hudson job is configured to deploy to Artifactory server (specified previously) 
       and one of repos available – a nice drop-down list allows to choose it:

        Hudson Job Config

When (and if!) job finishes successfully – all artifacts archived during the build (<archivingDisabled> should be set to “false” in job’s “config.xml” but that’s a default value) will be deployed by Hudson to Artifactory in one go:

Hudson Deploy 

It’s not truly atomic (if the process fails in the middle for some reasons – my guess is nothing would be un-deployed) but it’s still much better than what Maven does by default: deploying each artifact the moment it is ready (so if build process fails in the middle – some newer artifacts would be deployed already while some would stay in the previous version).

As you see, in this sense – Hudson’s way of deploying to Artifactory is much better as it only starts when the build has finished successfully. On top of that, for some weird reason Maven’s traditional deploy has let me down recently with:

Error installing metadata: Error updating group repository metadata
The requested operation cannot be performed on a file with a user-mapped section open

Seems to be some corruption issue that I couldn’t solve.
But "Ok" – I thought to myself – "One more reason not to use ‘mvn deploy’"

Now, does it scale?

After all, Hudson needs to deploy all created artifacts – what if there are too many of them?
In our case, it scales pretty well and there’s no problem whatsoever – our biggest job is publishing 170+ artifacts this way and it works just fine.

Ok, so what else does it actually do?

A lot. First of all, you now have a link to Artifactory in Hudson’s job: 

Hudson Artifactory link

Once we follow it to Artifactory – we get to a page where all build environmental data is stored: 

 General Build Info

So we have a "Properties" section here with JVM and OS versions recorded (though I wish there were some more), a link back to the Hudson job (I told ya those two started talking to each other!) and, most importantly, "Published Modules"

 Published Modules

For each published module – all its dependencies are recorded as well if we ever need to go back in time and figure out what dependencies do we need to re-create the module: 

Published Modules Dependencies

Following "Show In Tree" link we come to the usual artifact’s location in one of our repos where there’s now a new "Builds" tab: 
 
Builds

As you see – we now have an exact, bi-directional and traceable information about all jobs that ever deployed our artifacts.

And, like I said, I think it’s a lot. Since now we know for each artifact how it ended up being in Artifactory, by whom and when. We know which job has created it and we know what else was published by this job. For me it’s like a difference between my dady’s old garage (where you can find everything but nobody has any idea how things came along) and my mom’s kitchen (where every little thing has an origin and owner).

Don’t you love it already?

 

Properties

Historically, Maven doesn’t add much information to an artifact when it’s deployed. Nor does it offer any way to do so. Of course, a certain amount of metadata is added to each *.jar created (like it’s original POM) and each artifact has a traditional
<groupId>:<artifactId>:<version>:<classifier> coordinates (which is a huge improvement since Ant, if we really want to look back for a moment).

But, unfortunately, it only goes so far.

What if we want to mark or tag or label (pick up your favorite name) an artifact ?
A group of artifacts?

How about setting a "product=true" property to those artifacts that are final products (and not intermediate jars) ? We may talk a lot about artifacts and things but after all – people need working products, right? Those having "qa.status=passed" label on them. Or at least "qa.status=ok", may be.

As we can "label" e-mails in Gmail (surprisingly, some people don’t – I think they don’t know what they’re missing) or "tag" Delicious links – I would love to do the same with artifacts!

Some of them I would like to label manually, like QA steps in product lifecycle:
"qa.status = New => Accepted => Rejected => Passed => Graduated (?)"

Other properties I would like to be set automatically, when artifact is deployed: "build.number=35", "product=true" (if artifact is a ZIP file), "jvm=1.6" and the like.

Not surprisingly, there are two ways to set properties in Artifactory:

  • Manual
  • Automatic

The manual process is demonstrated here and, basically, it goes like this:

  • Define a property set: qa.status, qa.version, qa.anything
  • Choose a possible value for each property: any value, single-select, multi-select
  • Update your repo definition to make this property set available for it
       Watch out! If you miss this step – it will not work (happened twice to me)
  • For any artifact or folder in the tree – go to the new "Properties" tab and add a property

I agree, it’s more similar to Outlook “categories” (than to Gmail labels) and is a little bit involved but .. ok, that’s how it works for now. May be it’ll improve.

Anyway, being a software developer for life – I’m naturally more interested in things happening automatically. So how do I set a property on artifact during the build process?

I want to specify a POM <property> that will become an Artifactory property!

The answer is matrix-params:

<distributionManagement>
    <repository>
        <id>qa-releases</id>
        <url>
http://srv/artifactory/qa-rel;buildNumber=${number};rev=${rev}</url>
    </repository>
</distributionManagement>

As you see, it is simply a pair of arguments added to the deployment repo definition. Their values are usually taken from regular Maven properties and can be updated by any POM.
For example, to implement our “Products vs rest of artifacts” vision – all we need to do is to add a "product=${product}" matrix param:

<distributionManagement>
    <repository>
        <id>qa-releases</id>
        <url>
http://srv/artifactory/qa-rel;product=${product}</url>
    </repository>
</distributionManagement>

Some top-level <parent> POM will have it set to "false":

<properties>
    <product>false</product>
</properties>

.. but
those POMs packaging a final product will have it set to “true”:

<properties>
    <product>true</product>
</properties>

Can it be any simpler than that?!

Here’s a blog post demonstrating the same technique for the purpose of artifacts staging and promotion through tagging.

Today, there’s one problem here, though – if we use Hudson integration and switch to "Hudson deploy" (see above) – this <distributionManagement> tag isn’t worth a lot, is it?

And there’s no way to set up any matrix params from Hudson job configuration, where deployment repo is specified. The workaround is simple, though – one just needs to edit job’s “config.xml” (.hudson/jobs/JobName/config.xml) file manually and restart Hudson or “Manage Hudson” => "Reload Configuration from Disk":

<publishers>
    <org.jfrog.hudson.ArtifactoryRedeployPublisher>
        <details>
            <artifactoryName>
http://srv/artifactory</artifactoryName>
            <repositoryKey>
qa-rel;product=${product}</repositoryKey>
        </details>
        <deployArtifacts>true</deployArtifacts>
        <username>..</username>
        <scrambledPassword>..</scrambledPassword>
    </org.jfrog.hudson.ArtifactoryRedeployPublisher>
</publishers>

The bug is opened so I’m sure it’ll be fixed soon.

Ok, so we have our properties (tags, labels) set – now what? How do we use them?
That’s exactly what the next slide is about …

 

Smart Searches

In Gmail, searching for labeled mails is a matter of typing "g+l+label" (btw, I much preferred the “Labs” version over the “graduated” one). In Artifactory it’s a little bit involved (again) but that’s due to the fact that Artifactory searches are much more capable.

I believe options provided today would satisfy the most demanding (and esoteric) "querist":

  • Quick Search
  • Class Search
  • GAVC Search
  • Property Search
  • POM/XML Search

The first three are pretty obvious and very helpful indeed. I use GAVC Search most of the time, and a Class Search occasionally. But it still amazes me how fast Artifactory scans through its indices to locate all instances of, say, Scanner class:
 Scanner Search

It is a new Property Search we’re after today – it allows combining a query composed of a number of properties.

Like searching for all artifacts where "qa.version=1.0.1" and "qa.status=In QA".
Or, simply put, “What’s being checked today for the upcoming ‘1.0.1’ release?”

QA Search

It doesn’t matter how properties were set (either manually or automatically) – we can search for all of them! For example, "build.name" and "build.number" are sent by Hudson automatically so we can search by "build.number" as well: 
 Build Number Search

Search results can also be added or subtracted from each other – this is useful when they need to be either expanded or filtered with additional queries. They can also be saved for later use to perform a single operation on all results, where options are “Move”, “Copy” and “Delete”.

The blog post I’ve mentioned already shows exactly that – how artifacts can be

  1. Searched for
  2. Promoted to another repository with "Move" / "Copy" operation

As you see, using Property Search anyone can find what he’s looking for (assuming properties were set in the first place, of course): be it a QA person, looking for the last binaries to download and test or a Dev manager, looking for the binaries being QA-ed now.

The last advanced search is POM/XML Search allowing to search through all POMs (in all or specific repos) with XPath queries. I’ve used it yesterday trying to find out which POMs were using some specific plugin. Normally, I just run a textual search on “pom.xml” files through the whole “trunk” and it takes .. well, quite a while, of course. With Artifactory – it can be done smarter and faster:

XML Search

As you see, "/project/build/plugins/plugin/artifactId" search does the job.
And, of course, it takes less time than an old-school Total Commander textual search
(I don’t even need to measure it – it’s seconds vs minutes!)

So far I didn’t encounter a case where Artifactory searches were not sufficient.
They’re always smart (though I would call it "capable") enough.

 

Watches

I suppose this one is the easiest to describe and, in fact, there’s probably no need to describe it at all. One can set up “watches” to be notified by e-mail when certain repository, folder or artifact has a “create” or “delete” operation performed on it:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following events have recently occurred in Artifactory on items you are watching:

Sun Jan 10 06:30:21 IST 2010 [user-name/XXX.XXX.XXX.XXX] [CREATED] libs-snapshots-local:com/clearforest/ProductsPage/8.0-SNAPSHOT/ProductsPage-8.0-SNAPSHOT.pom
Sun Jan 10 06:30:18 IST 2010 [user-name/XXX.XXX.XXX.XXX] [CREATED] libs-snapshots-local:com
/clearforest/ProductsPage/8.0-SNAPSHOT/ProductsPage-8.0-SNAPSHOT.war
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Ironically, about a year ago – I was practically dying to get this notification: somehow, trunk POMs were overridden by an older versions and I suspected someone from the dev team running “mvn deploy” on an outdated sources (but this was not the case, actually).

So although I get quite a lot of e-mails from my “watches” – I just keep them in case anything like that will ever happen again. And of course, it becomes even more necessary when certain repos are used for special purposes, like in staging and promoting scenario.

 

Conclusions

I think “Power Pack” is a critical add-on to what Artifactory offers.

Being able to integrate it with Hudson, set custom properties and search for them is what makes it a nice, organized and watched storage rather than a kitchen sink of everything that happened to be downloaded from somewhere on the Internet.

Whether you have it or don’t – Artifactory surely delivers! But the questions are:

  • How aware are you of what’s happening?
  • How easy it is for you to dig through the mess and find what you need?

May be it’s just me, but I just love knowing what’s going on and when. It keeps me in control of things and not the other way around which I believe is a good thing, in general.

Happy building!

 

Artifactory 2.1.3 upgrade

09 Jan

I was waiting for Artifactory 2.1.3 upgrade since it includes a long-waited feature of being able to set an include and exclude patterns on virtual repositories.

Finally, I’m able to stop Maven and Artifactory from looking in external repos for our company’s snapshots updates. Setting an exclude pattern of “com/company/**” on “remote-repos” does the job. Great!

Also, I’ve started to use another neat feature (that was introduced in version 2.1) of being able to move artifacts to another repository. I’m not sure JFrog developers had my scenario in mind when they were working on it (they crafted it for staging and promotion – we’re not there yet .. ) but here it goes: from time to time it’s time to cleanup all old snapshots and start from day zero. I mean – re-build everything from scratch as if there were no yesterday’s snapshots in the company’s repository manager. Since we rely on snapshots heavily (create hundreds of them in different products) the good question is – are we still able to create all artifacts having absolutely no snapshots in the repository? Is our build order correct? Do we have any cyclic dependencies (that are perfectly hidden when they’re always yesterday’s snapshots to use)?

One way to answer this question is remove all snapshots from Artifactory and fire up the Hudson jobs that create all our products. If build order is wrong (and when there are many products built with different layers of “infrastructures” developed by different teams – it’s not that hard, really) – the job will fail. Since it has no “yesterday’s” snapshot to rely on. So it either works or it doesn’t.

I did it couple of time (still thinking of a way to do it automatically on a nightly basis – any suggestions here?) but what stopped me from doing it more frequently is I was simply afraid of deleting all old snapshots. What happens if some important snapshot was deployed once and I’ll find out I really need it after deleting it ?

Not any more since Artifactory 2.1!

After all – we have something called “Recycle Bin” since Windows 95 so I’ve created a standalone repository called “trash” that is not part of virtual repo in Artifactory (that are viewable to people in the company) and I move all old snapshots there first. That’s it!

I think it’s a critical check for any complex product since each time we do it – we find out our build order is slightly wrong and we do have cyclic dependencies here and there (with ~300 POMs spread all over SVN tree – you can’t monitor each of them).

Now, Hudson integration is coming next!

It’s a big thing, of course, but makes me give up on Maven’s deployment mechanism and rely on Hudson’s one. So far I’ve tried to keep our Hudson jobs as simple as possible without any additional steps (except invoking other jobs) so that all “build” logic will be stored in POMs for people to see it. If it’s spread between Maven and Hudson – my assumption is that it will be harder for people to see “how it works” and “what’s going on here” (questions I’m already asked a little bit too often).

But .. They way Hudson does a clean deploy to Artifactory will all metadata uploaded really makes me re-consider this approach and make our Hudson jobs “heavier”. This time it’s worth it, I think.

Some more recent Artifactory tips:

    P.S
    Since version 2.1.3 Artifactory became even more stricter regarding POM’s “health status”. It’s good JFrog folks care a lot about what is stored inside Artifactory and work really hard not to allow any garbage “in”.

    After making an upgrade some of our builds failed since it appears there are some pretty old POMs that have UTF-8 byte issues and Artifactory isn’t willing to serve them any more (they were already inside the storage but couldn’t be downloaded by Maven).

    To fix that I had to:

    • Download POMs manually from repo1
    • Remove some characters in developers names XML parser was complaining 
         about (no offense, guys)
    • Save them as “UTF-8 + BOM” in EditPlus
    • Delete old copies that were already stored in Artifactory 
    • Deploy new “fixed” copies instead

    Here are the POMs that were fixed that way (updating our <dependencies> to rely on newer <version> didn’t help – those dependencies hardcoded somewhere deeper, inside Maven itself):