views:

60

answers:

4

Hi,

I have this dependency situation:

C (->) B -> A -> C

where B->C means that module C has a dependency to B in its pom.xml.

Well...i want to use a class from B in C so i have to put C->B BUT i receive a cycling error as you can see above...

Do you see any workaround? I CANNOT move that class from B in C because it already uses some classes from A and there will be cycling again.

I've tried to declare an interface IAlfa in C and implement it in B(Alfa) (but when i want to use it i need to instantiate like:

IAlfa alfa = new Alfa() 

so again i need to import Alfa from B.

What do you think?

Thanks.

+2  A: 

Such a situation seems to be a symptom of bad class/library organization.

It seems extremely strange that you would legitimately get into such a situation (Feel free to explain more precisely how you organized your classes if you want to discuss that)

If you have full control of the composition of A, B and C, you can do at least the following things:

  1. Merge A, B and C into a single library and be done with it. (Not recommended - it will make good modularization nearly impossible)
  2. You needed to use a B class in C in order to do a work W. Do that work in library D instead.
  3. Move the B class you wanted, 'and' the classes from A from which the B class depends, in C.
jhominal
A: 

Most times when I have to solve a problem like this, I explicitly declare the dependencies in the POM file, then add an EXCLUDES clause to dependencies in question. That way I'm sure I get the versions I want. Its more work, but it saves you from having 12 copies of commons-logging in your WAR file. ;)

mezmo
+1  A: 

There are a couple of things to do.

First starting from artifact C all the way through A use mvn dependency:analyze to verify what dependencies are really being used and not just declared in your pom.xml, this Maven plugin is very useful as a first step to debug your dependency tree. It might be the case that there are some dependencies that are just declared but not used, and/or used but not declared, etc.

Once you verify the real state of your dependency tree then you should start to move things, in case your dependency analyze step hasn't shown any major problem that can be easily solved.

I don't know how deep your problem is, since you haven't posted any configuration and or code snipped, but you can try in C's pom.xml something like:

    <dependency>
        <groupId>org.sample</groupId>
        <artifactId>module-b</artifactId>
            <exclusions>
              <exclusion>
                <groupId>org.sample</groupId>
                <artifactId>module-c</artifactId>
              </exclusion>
           </exclusions>
    </dependency>

To exclude the C dependency from B's that's inherited transitively from A's.

StudiousJoseph
+2  A: 

This should not happen. What I usually do is have a module structure like this:

root
    - api (interfaces, enums and custom exceptions only)
    - impl (standard implementation of api -> dependency to api)
    - server (runtime dependency to impl, compile time to api only)
    - client (dependency to api only)

Let's say we have an interface Person in the api module. Now in the impl module, you'd have a class JpaPerson, because impl uses JPA. However, those are details that client and server don't need to now, so you should not (and in my design can not) do new JpaPerson() in either client or server. Instead you'd have an interface (also inside api) called PersonFactory (sounds awful when you're talking about people, maybe name it more human-friendly), that has methods like this:

public interface PersonFactory{

    Person findPersonBySsn(String socialSecurityNumber);
    List<Person> findPersonsByName(String firstName, String lastName);
    List<Person> findPersonByBirthDate(Date birthDate);
    Person createPerson(
        String firstName, String lastName,
        Date birthDate, String socialSecurityNumber
    );

}

Again, implement this interface in the impl module, but reference it as an interface only in the other modules. Then use dependency injection to wire things together (Spring, SEAM, EJB3, whatever).

The situation you are mentioning is a result of code doing things that are beyond it's responsibility (read about separation of concerns).

The great advantage to this design is that you can refactor all modules (except api) individually, without changing code in the other modules. So if you want to switch from jpa to classic hibernate, you just need to edit impl. If you want to switch the server from axis to cxf, just change server. This makes things a lot easier. And of course you don't get circular dependencies.


EDIT:

a simple (simpler) solution for you would be to introduce a module d that is included as a dependency in a, b and c. Move all common code to d. (Actually rename this d to a and a,b,c to b,c,d).

seanizer
Thanks...very usefull explanation...
Cristian Boariu