8

I have a server application and two Swing-based client applications. We're developing them in eclipse and there's a separate project for each.

Many classes are shared. For example, the my.server package has some classes for both the server and the clients while others are for the server only. Although I prefer to put them in the same package because they are closely related and some of them rely on package visibility, I don't want to distribute classes that an application does not need as not only would it bloat the file size, but also it would be a security risk.

At the moment, each of the server and the clients has the same jars, which is a mess. Ideally, I'd like to automatically create jars based on dependency as following.

Server:

  • server.jar: classes used by Server only
  • server-client1-common.jar: classes shared by Server and Client 1
  • server-client2-common.jar: classes shared by Server and Client 2

Client 1:

  • client1.jar: classes used by Client 1 only
  • server-client1-common.jar: classes shared by Server and Client 1
  • client-common.jar: classes shared by Client 1 and Client 2, but not Server

Client 2:

  • client2.jar: classes used by Client 2 only
  • server-client2-common.jar: classes shared by Server and Client 2
  • client-common.jar: classes shared by Client 1 and Client 2, but not Server

I realize that you can do this manually using ant, but it would be a maintenance disaster. Is there a tool that takes care of such dependency automatically?

Tom Tucker
  • 11,676
  • 22
  • 89
  • 130
  • 1
    If you are using 'maven' for the build process, check this discussion: http://stackoverflow.com/questions/2424015/maven-best-practice-for-generating-multiple-jars-with-different-filtered-classes – bchetty Feb 08 '12 at 11:30

4 Answers4

3

What do you mean by "maintenance disaster"? If you create an ANT script, just run it and it will compile and pack the jars for you.

As a more robust alternative, you might use maven. For something more lightweight, the built-in eclipse export tool might work.

Marcelo
  • 4,580
  • 7
  • 29
  • 46
  • By maintenance disaster, wouldn't you need to update the ant script every time you create a class or interface? – Tom Tucker Jan 30 '12 at 15:13
  • 1
    You probably should create at least three separate packages - `server`, `client` and `shared` and just add those to the script. – Marcelo Jan 30 '12 at 15:15
  • That's the problem I'm having - in the same package, some classes rely on package visibility, some are shared and others should not be shared. – Tom Tucker Jan 30 '12 at 15:17
  • 3
    If you rely on packet visibility on classes that should be in separate packages (one running on the client and other on the server, for instance), you should re-think your architecture... IMO you are complicating things by not organizing them properly and trying to find a tool that will organize it for you during build time. – Marcelo Jan 30 '12 at 15:25
  • 1
    Our project (1,000,000 lines) is structured like this: com.example.[client|server|shared].individual.package.names. The ant script is huge (simply because of the size of the project), but it uses wildcards in the jar step to create the appropriate jars - no updating with each new class. A problem that cropped up with this scheme was the use of client code on the server or vice-versa. To prevent this, we use VerifyDesign ([link](http://ant-contrib.sourceforge.net/tasks/tasks/verifydesign.html)) in our local & incremental builds. – KJP Jan 30 '12 at 16:27
3

I cannot present you with a ready-to-go solution. Here's an idea though: create an annotation or a set of annotations like this:

@jarselector(types='server')
class ServerOnly {
   ...
}

@jarselector(types='server,client1')
class ServerAndClient {
   ...
}

Then create your own ant task by extending the jar task (or maven plugin) or write your own task, that takes this annotation and packages classes according to the annotation, which you would then be using as a filter.

You would only have to create the task once - I've done it in the past, it is less complicated than it sounds and the problem sounds big enough to warrant the effort.

Afterwards you have to annotate all your classes once (or depending on your implementation only those classes the clients need, or only those that are not shared by every jar etc.). Whoever sees a class can see immediately what it is used for. When creating a new class you can easily add the annotation.

I really don't think there is a ready made ant task or maven plugin that does this.

Alternatively - if you really cannot change your package structure - you could also use multiple source directories to keep the packages but split the files in different directories. Eclipse doesn't care how many source directories you use. You would then need to adapt your build tool just once for the source directories and could then sort the files that way.

Yashima
  • 2,248
  • 2
  • 23
  • 39
1

Have a separate Eclipse project for each JAR that you're going to create. Then set up the dependencies on the "Projects" tab of the Build Path, for each of the top level projects. So, the "server" project will have "server-client1-common" and "server-client2-common" listed as required projects. And so on.

I've seen this model used by a number of different organisations, and I've always thought that this was the "industry accepted" way of doing it. It just works!

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
1

One of the best practices regarding building applications is to have one jar per project.
Maven, for example, uses this as default. You can trick it to do otherwise, but it is better to join them instead of "fight" them.

http://maven.apache.org/guides/mini/guide-using-one-source-directory.html
http://www.sonatype.com/people/2010/01/how-to-create-two-jars-from-one-project-and-why-you-shouldnt/

So, in your case you should create 6 projects: Server, Client1, Client2, ServerClient1Common, ServerClient2Common, ClientCommon

In order to select the classes needed, I don't think there is a tool, and probably you should know better what is the common functionality. Create the Common projects, and add them to the build path - dependencies. Then start moving your classes into the Common project, leaving them in the same package.

For example, create ServerClient1Common project.
For Client1, go to Configure Build Path -> Projects. Add ServerClient1Common. Remove all references to Server Project.
For Server, go to Configure Build Path -> Projects. Add ServerClient1Common. Remove all references to Client1 Project.

You should now have a lot of missing classes/imports. Try to solve them one by one.

At the end, you should be able compile the 3 projects and to obtain the jars you mentioned.

PS: Other strategies (like one uber-project with different build targets, or 3 projects with entwined ant/maven builders) are messier. There is maybe one exception - another way of splitting the classes, but I do not know if it applies to you: client1.jar, client1-interface.jar, client2.jar, client2-interface.jar, server.jar, server-interface.jar. This way you could use 3 projects with each having two target jars. To run client2.jar you will need server-interface.jar

Alin Stoian
  • 1,122
  • 1
  • 12
  • 24