Wednesday, October 10, 2007

Announcement: Version 99 Does Not Exist

Last update: 2011-12-19

Update 2011-08-15: DNS for Version 99 is offline.

In a previous post I announced no-commons-logging (a.k.a. commons-logging version 99.0-does-not-exist). After a request to add no-commons-logging-api I immediately realized that this can be generalized. So here it is: Version 99 Does Not Exist.

Features
Version 99 Does Not Exist emulates a Maven 2 repository and serves empty jars for any valid package that has version number 99.0-does-not-exist. It also generates poms, metadata files and of course the appropriate hashes.

For example the following links will give an empty jar, its pom and the maven metadata for commons-logging.

Why?
To get rid of dependencies that maven 2 insists on including on your classpath (like commons-logging while you want to use jcl-over-slf4j).

How do you use it?
First of all: if you were using no-commons-logging before, you do not need to change anything! Version 99 Does Not Exist is fully backward compatible with no-commons-logging. Otherwise, read on.

In your pom.xml declare the following 2 things: 1) the Version 99 Does Not Exist repository, and 2) for each jar that you get but do not want, declare a dependency with version 99.0-does-not-exist.

So, for example, if you do not want to be bothered with commons-logging, include the following in your pom.xml:

<repositories>
    <repository>
        <id>Version99</id>
        <name>Version 99 Does Not Exist Maven repository</name>
        <layout>default</layout>
        <url>http://no-commons-logging.zapto.org/mvn2</url>
    </repository>
</repositories>
<dependencies>
    <!-- get empty jar instead of commons-logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>99.0-does-not-exist</version>
    </dependency>
</dependencies>
When? Right now. As I received questions on how stable this service will be, I hereby promise to keep this maven repository on-line for at least 5 years (or until no-ip stops offering their free dns service). Read further if you want to run Version 99 Does Not Exist yourself.

How?
Version 99 Does Not Exist is implemented in a single file as a Camping application.

Where is the code?
If you want to run it yourself you'll need the following: Version 99 Does Not Exist download (rb file, 4Kb, MIT license), Ruby, Ruby Gems and Camping. Version 99 Does Not Exist is started with a simple camping version99.rb. Good camping!

Update 2007-11-06: The empty jar is no longer completely empty as 'mvn site' fails on it. Thanks to Stefan Fußenegger for the report.

Update 2007-12-14: The repository was off line for a day or so because I goofed while switching internet provider. Now, it is not only working again, but reachability is better then ever: 10 Mbit/s up and down!

Update 2008-02-09: Version 1.2: another update to the empty jar. Sasha Ovsankin reported that the compiler could not open it. The jar now contains a valid manifest. Thanks Sacha!

Update 2009-05-01: During my move from Amsterdam to Haarlem the server has been off line for a day or so. I am now on ADSL so my internet connection is a lot slower. If anyone want to run a mirror, I am happy to set up a rotating DNS.

Update 2009-07-24: Version 1.3: artifacts with a groupid that contain a dot are now supported. Éric Vigeant, thanks for the bug report!

Update 2009-10-17: Version 2.0: Éric Vigeant's problems were still not over. Now removed metadata support, this will hopefully make some proxies behave better.

Update 2011-08-15: DNS for Version 99 is offline.

Update 2011-12-19: I just found an alternative with almost the same name: version99.qos.ch. It is a static maven repository with a limited number of version 99 jars.

Update 2019-11-15: Fixed link to original Camping appliction. The Camping links now point to wikipedia.

33 comments:

  1. Wow, cool! For something like this camping seems to be really useful. Seems like te right tool for the right job!

    ReplyDelete
  2. I probably miss something but isn't

    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl104-over-slf4j</artifactId>
    <scope>runtime</scope>
    </dependency>

    exactly what you want?

    ReplyDelete
  3. @joern: Thanks Joern. After many days of plain misunderstanding and struggling with maven bugs(?) I gave up and wrote this workaround.

    But I'll try your suggestion next week.

    ReplyDelete
  4. @joern: Actually your solution is different!

    The "provided" scope makes sure commons-logging is not put in the distribution; but commons-logging classes will still be on the classpath at compiletime or when running tests which might not be what you want!

    ReplyDelete
  5. Ah! Thanks Peter. Indeed you are right. I had forgotten already. That probably illustrates that Maven is a tad too difficult, or put otherwise: Maven is only suitable for people with very large brains, or yet another way: its for people that do not have much else to do :)

    ReplyDelete
  6. Hey thanks for your effort here! I just your little repository and figured one thing out: if you are working with ranges like

    <dependency>
    <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId>
    <version>[5.0,)</version>
    </dependency>

    you are screwed, as maven will add mysql-connector-java-99.0-does-not-exist to the classpath. everybody using your hack should consider this!

    Another thing: I also get this when running `mvn site`:
    error: error reading repo\commons-logging\commons-logging\99.0-does-not-exis
    t\commons-logging-99.0-does-not-exist.jar; error in opening zip file

    I don't know which task fails and I don't have to much time for fiddling around with maven right now, but I guess it is expecting some files in the jar files and throws an exception as the jar/zip is empty. Probably adding a simple README to the file would make this plugin (and maybe others) happy.

    Regards, Stefan

    ReplyDelete
  7. the task that fails with commons-logging-99.0-does-not-exist is compiler:testCompile. I added a README to the jar file and everything works fine again ... so I guess I have too much time ;)

    ReplyDelete
  8. Thanks Stefan! I'll add a readme to the jar as soon as possible.

    About the open ended versions: I would never like to work that way, far to risky! But in a way, all software will eventually be obsolete, so it makes sense ;)

    ReplyDelete
  9. hi erik,

    isn't it kind of frustrating to think that all software that you write will be obsolete in the end ;)

    I like working with open ranges in a very early development/prototype stage ... and I just like to live on the edge ;)

    In deed, I agree that using open ranges is too dangerous. Tough I'd use versions like [1.2.3,1.3) or [1.2.3,2.0) in order to get the latest bugfixes etc.

    I just forgot to close one of those ranges and ended up with a useless mysql connector. Therefore I wanted to drop a (short?) note for everybody.

    ReplyDelete
  10. Ha! It is no less frustrating then the end of life itself! ;)

    Thanks for the note though.

    ReplyDelete
  11. hi erik,

    i just came back to see if you patched the file and i've seen that you did today ... thanks a lot!

    regards, stefan

    ReplyDelete
  12. Hi Erik,

    Thanks for the great hack. For some reason, it didn't work in my build because the compiler would reject the JAR file. Here is the link to a file that works for me:
    http://www.codebistro.com/commons-logging-99.0-does-not-exist.jar

    Thanks,
    -- Sasha

    ReplyDelete
  13. Hi, could you please do the same for commons-logging-api module? Thanks.

    ReplyDelete
  14. Very useful!

    I've hit on another use case that the repository does not cover yet. If the group id has more than one folder, this repository does not work.

    I need:
    org.apache.commons:
    com.springsource.org.apache.commons.logging:
    99.0-does-not-exist

    Thanks again for facilitating SLF4J.

    ReplyDelete
  15. WOW, very fast turnaround!
    Thanks, this solved my original problem.

    However (maybe due to the fix I asked), I now run into another problem with maven plugins.

    I'm using Nexus as a mirror for everything including pluginRepository. However, I cannot add the 99-does-not-exist repository in that list because all plugins stop working.

    This is due to the maven-metadata.xml files which list version 99-does-not-exist. Are those absolutely necessary with maven2?

    I'm assuming somebody needed them, since you are generating them.

    I was wondering if it was possible to have a version of the repository without the metadata files (EX: http://no-commons-logging.zapto.org/mvn2-no-metadata/...)

    Thanks for the great (and quick) work!

    ReplyDelete
  16. Hi Erik, thanks a lot. But today -after some time out on development- I can no longer use mvn site:site if your repo is in my pom.xml. I got the following error by maven: "[INFO] The skin does not exist: Unable to determine the release version

    Try downloading the file manually from the project website.

    Then, install it using the command:
    mvn install:install-file -DgroupId=org.apache.maven.skins -DartifactId=maven-default-skin -Dversion=RELEASE -Dpackaging=jar -Dfile=/path/to/file

    Alternatively, if you host your own repository you can deploy the file there:
    mvn deploy:deploy-file -DgroupId=org.apache.maven.skins -DartifactId=maven-default-skin -Dversion=RELEASE -Dpackaging=jar -Dfile=/path/to/file -Durl=[url] -DrepositoryId=[id]


    org.apache.maven.skins:maven-default-skin:jar:RELEASE"

    ReplyDelete
  17. Hey! I selected Name/URL - not Anonymous! Anyway... Now with Google Account...

    Stefan

    ReplyDelete
  18. Been using this method successfully for a while, but started getting the following today:

    Couldn't find a version in [99.0-does-not-exist] to match range [1.5.0,2)
    org.slf4j:log4j-over-slf4j:jar:null

    Anyone else having this problem?

    ReplyDelete
  19. Hey Erik, any comments on the two errors form me an Thom?

    ReplyDelete
  20. Stefan, I have no idea what that means.

    Thom, you should also include some other 'real' repositories i your repository list.

    ReplyDelete
  21. Hi Erik, I think the problem mentioned all come from what I described in my earlier comment.

    When Maven checks for newer versions of plugin (or for dependencies with open-ended version intervals) it asks the repositories the version numbers.

    Since your repository always returns that there is a version 99, it is always the one selected, but then the jar is empty...

    This problem probably occurs since the fix you made to accomodate jars with dots in the groupID. (Sorry I was the one who requested this fix...)

    Is there a way to return that your repository contains a version 99 only when it is requested explicitely... (I don't know the maven protocol...)

    ReplyDelete
  22. Released 2.0 with removed support for metadata. Please let me know if it works.

    ReplyDelete
  23. I may be missing something, but isn't it much easier to simply exclude the nested dependency, as in:


    org.hibernate
    hibernate
    3.2.6.ga


    commons-logging
    commons-logging




    This even works with dependencyManagement!

    Regards
    Walter

    ReplyDelete
  24. sorry... have to escape the > and < ;/

    <dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate</artifactId>
     <version>3.2.6.ga</version>
     <exclusions>
      <exclusion>
       <artifactId>commons-logging</artifactId>
       <groupId>commons-logging</groupId>
      </exclusion>
     </exclusions>
    </dependency>

    ReplyDelete
  25. Walter, when you declare an exclusion like that, you exclude commons-logging only for hibernate. So to get this right, you must follow all your dependencies, and all their dependencies, and their dependencies, etc. Each dependency that declares commons-logging must then be listed in your pom.

    If you have ever tried this (I have) you get tired very quickly. Your poms become very large and fragile.

    ReplyDelete
  26. Erik, you are right... it may get tedious. But it has the advantage that there is less black magic involved. You can see which packages still contain that cruft... and they're getting less and less ;)

    We actually switched in a large project from 99 to exclusions.

    ReplyDelete
  27. Walter, good for you! I'll celebrate the day that version 99 is no longer needed! You're already there apparently :)

    ReplyDelete
  28. hi there,

    stumbled opon the same troubles... however, I realized that if you declare the logback/slf4j bridge dependencies before your other dependencies in the pom.xml they will be handled first!

    no need for a hack ;-)

    e.g.



    ch.qos.logback
    logback-core
    0.9.18


    ch.qos.logback
    logback-classic
    0.9.18


    org.slf4j
    slf4j-api
    1.5.10


    org.slf4j
    jcl-over-slf4j
    1.5.10


    org.slf4j
    log4j-over-slf4j
    1.5.10


    some.evil.lib
    using-commons-logging
    ...

    Cheers

    ReplyDelete
  29. Hi Erik,

    just wanted to tell you that everything is working great! I added back your repository in my nexus instance after you made the fix (v2.0) and I didn't have any of the plugin problems I had before.

    So thank you once again for the quick fix and solution to commons-logging problem. Sorry for taking so long to give you feedback, I guess when no one complains, you can assume everything is working fine!

    ReplyDelete
  30. I am having some curious problems.
    http://stackoverflow.com/questions/5396384/dependency-mechanism-overriding-transitive-version

    ReplyDelete
  31. Gradle does this pretty nicely these days: (killer feature :) )

    configurations {
    all*.exclude group: "commons-logging"
    }

    And be done with it :)

    ReplyDelete