Creating client jar file for RESTful web service

We are building a RESTful web service in STS with Gradle plugin. Gradle builds the war file just fine and now I want to create a client jar file that contains the domain objects in the com.mycomp.domain package so a client can bind the json to them. Here is my build.gradle:

apply plugin: 'war'

sourceCompatibility = 1.7
version = '0.0.0.1'

dependencies {
	compile "org.hibernate:hibernate-validator:4.2.0.Final"
	providedCompile "javax.servlet:javax.servlet-api:3.0.1"
	
	testCompile group: 'junit', name: 'junit', version: '4.+'
}

war {
	baseName = 'service'
	version = version
}

// client.jar
task client(type: Jar) {
    from  (sourceSets.main.output) {  
        include "com/mycomp/domain/**"
    } 
}

I look in build/libs and there is only the war file. What am I doing wrong? Thanks.

Which what tasks to you initiate your build. BTW: Instead of using package separation here, I would put the common code in a separate sourceSet.

cheers,
René

Thanks, Rene, I use ‘gradle build’

The war plugin is handling adding the appropriate task dependencies for creating the war file that are run when you run the build task. With your current build script, the jar would not be created unless you explicitly run the client task. If you declare your client jar task as creating an artifact, the task will run when expected.

artifacts {
    archives client
}

I get this error when I add

    artifacts {
    archives client
}

to the script:

Could not find property ‘client’ on org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@72ce812e.

The property ‘client’ doesn’t exist until you have declared your client task that creates the jar.

As written, you would need to put it below everything that you posted.

EDIT
Adding the artifacts declaration will make it behave more like you expect. However, I do agree with René that a separate sourceSet would be better.

It works as I expected when I added to the end. As to the separate sourceSet, what would be the reason or advantage. Is a source set just a different declaration of files pointing to the same package? Thanks for thelp, it’s very much appreciated.

Using a separate sourceSet is just a different way of modeling those different source files in your project. It creates a much stricter separation than just including and excluding files with a pattern.

One of the most common examples is that a typical java project has both src/main/java and src/main/test. The test sourceSet separates test classes from the main classes and makes sure that only the production classes get shipped.

Your case is similar in that you want to ship some classes to the client. Often you will actually see this modeled as two projects, one that applies just the java plugin and builds a JAR. Both your web service and your clients would depend on this JAR. Just using a sourceSet would be a middle-ground between what you have and creating more than one project.

Some advantages of this type of setup is that you could ship anything to the client you want without packaging constraints or needing to be modifying the JAR includes (maybe something useful for the client that isn’t necessarily domain). It could also help you manage versioning the client JAR. If you are depending on the client JAR in your web service yourself, you can’t accidentally introduce a version of the web service with changes that are not compatible with the client JAR when you were not expecting clients to need an update. However, this could be overkill for your situation.

James,
2 projects would be overkill for the size of these web services, but I will keep that strategy in mind if we get a large web service to write. The separate sourceSet sounds like the way to go for most of our web services, but I cannot get my head wrapped around sourceSets. I have tried several ways to specify one but the best one I came up with didn’t allow me to change the name of the client jar. What I want is to create a war file for the web service and a client jar containing the contents of one (maybe more, bur rare) package. It sounds simple enough, but I could not figure out how to express it in Gradle. This was the closest I got, but the jar file would not get the baseName of the war file. It’s using the default war.baseName (name of project). What is missing here? Thanks again.

war {
	baseName = 'service'
	version = version
}


sourceSets {
    def jarTask = task(type: Jar) {
        baseName = war.baseName
        from main.src.com.abc.service.domain.output
    }
 
    artifacts {
        archives jarTask
    }
}

Take a look at this example.

apply plugin: 'war'

archivesBaseName = 'service'
sourceCompatibility = 1.7
version = '0.0.0.1'

dependencies {
    compile "org.hibernate:hibernate-validator:4.2.0.Final"
    providedCompile "javax.servlet:javax.servlet-api:3.0.1"
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

repositories {
    mavenCentral()
}

sourceSets {
    client {
        java {
            srcDir 'src/main/java'
            include 'com/mycomp/domain/*'
        }
    }
}

task client(type: Jar) {
    from sourceSets.client.output
}

artifacts {
    archives client
}

It’s basically your original script, with a couple modifications to use a source set. Really the only difference is that the isolation of the domain classes used in the client JAR occurs on the input source files rather than the output class files.

This is not exactly what I was recommending, but I have the sense that you don’t want to make any changes to the folder structure. I would really expect a folder structure matching what is shown in the User Guide: Java Project Layout, those last two being client in your case. That would allow you to rely on more convention and less configuration, but you do have flexibility to do this or not (or forget about sourceSets and just add the artifacts {} block).

As far as naming, you can use archivesBaseName and version, which will automatically be used when creating the WAR and JAR file. You don’t have to explicitly set each one in the jar and war configuration. However, you could also append client to the name if you wanted like this:

task client(type: Jar) {
    baseName += '-client'
    from sourceSets.client.output
}

Finally, just for completeness, when I mentioned multiple projects, I was thinking something along the lines of User Guide: Section 7.3. Your client JAR is basically the same as the API project in the example, but I get that you may not want a multi-project build for something very small.

The client jar in this case really should be a separate project. Primarily because it likely has completely different dependencies than your WAR. Using @jjustinic’s solution you would be publishing the client jar as an additional artifact, but the pom/ivy xml generated would be based on the default artifact (the WAR). You could conceivable get around this by using the new ‘maven-publish’ or ‘ivy-publish’ plugins and creating multiple publications, but you would have to do all the metadata manipulation stuff manually.

If you have no intention of publishing this artifact to a repository then you can ignore the statements above and go with @jjustinic’s solution.

Thanks, Mark. What we are doing is building a RESTful web service. Essentially we return objects from our Spring Controller which is annotated with @ResponseBody. Spring calls on a converter to convert the objects to Json (or Xml). We want to supply these same objects to the client, so they have the option to bind to the identical objects, which is why I don’t really see a need for separate projects. With every new version of the web service, there will be a corresponding client jar.
Given this scenario, do you still suggest a different project for the client jar?

This almost works. The compileClient fails because the dependencies are not available to it, so it can’t find the annotation @NotNull, for instance.
I looked at the User Guide: Section 7.3, and if this solution is unworkable, I will have to switch to that, I guess. It just seems weird to have to create a separate source directory just build a separate jar. With this gradle script, I essentially want to execute the jar command on the WEB-INF/classes directory after the war is built.

Yes. This is not a good example of what sourcesets are meant to model. This should be a separate project with its own resources, dependencies, and publications.

Yes, compileClient is going to fail if the code in the client source set has dependencies and they haven’t been declared. I haven’t seen your source code, so I didn’t make any assumptions about where you were using those dependencies.

You can certainly add a ‘clientCompile’ configuration and declare the hibernate validator dependency as part of that configuration, but this is really why a separate project was suggested. It’s getting to a point that there’s a lot of additional configuration to make things work like they would have just worked by convention with a separate project.

It seems like you’re looking at the output, saying that it’s not that far from what you want, and then trying to coax it into the right output (these classes in the WAR go in the JAR, just put them there). Instead, if you model the input like what you actually have (a library of domain objects you want to use in multiple other applications, such as your service and clients), you’ll get the desired output without much effort, and it won’t require additional configuration once someone says, “can we also do… ?”