Статьи

Один кувшин, чтобы управлять ими всеми

Путешествие вниз по переулку памяти
Еще в 1998 году, когда я был разработчиком C / C ++ и пробовал свои силы в Java, несколько слов о языке были, мягко говоря, раздражающими для меня. Я помню, волнуюсь об этом довольно много

  1. Почему нет достойного редактора для этого? C / C ++ было немало. Все, что у меня было для Java — старый добрый блокнот. 
  2. Зачем мне делать класс, когда все, что я хочу, это функция? Почему функция не была объектом? 
  3. Почему я не могу просто упаковать все в один zip / jar и позволить конечному пользователю запустить его двойным щелчком мыши? 

и несколько других.

В то время я обнаруживал, что часто упрекаю себя за то, что я не могу отказаться от своего «мышления на C / C ++» и придерживаюсь «Java-способа» ведения дел. Теперь, когда мы писали эту статью в 2013 году, спустя полтора десятилетия, неожиданно все эти ранние раздражения исчезли. Не потому, что я принял «Java», а потому, что ява изменилась.

В стороне от пустых разговоров, цель этой статьи — поговорить об одном из этих вопросов: «Почему я не могу просто упаковать все в один zip / jar и позволить конечному пользователю запустить его двойным щелчком мыши?».

Зачем нам это — один zip / jar — это исполняемый файл? 

Если вы — разработчик, с радостью пишите в своей IDE (я презираю всех, кто закодировал Java на Eclipse, NetBeans с самого первого дня и которому не приходилось кодировать на Notepad :)), при поддержке Google (я совершенно точно ненавижу все вы все, кому не надо было искать вещи в интернете до гугла ? :)), наверняка нет убедительного случая.

Однако сталкивались ли вы с ситуацией, когда

  1. Вас втянули в центр обработки данных, потому что парень выполнил ваши шаги по развертыванию, но ваше приложение / веб-сайт просто не будет работать? 
  2. Внезапно все переменные среды перепутались, когда «никто вообще не трогал» производственные боксы, и вы тот, кто должен «просто заставить это работать». 
  3. Вы сидите с заинтересованным лицом вашего бизнеса и недоверчиво смотрите на «исключение ClassNotFound» и были убеждены, что Java вам совсем не нравится.

Короче говоря, я пытаюсь сказать, что, когда вы находитесь в «относительном» здравомыслии своего dev-бокса / окружения, один исполняемый файл jar на самом деле ничего не делает для вас.
Но в тот момент, когда вы входите в сумеречную зону неизвестных серверов и ситуаций (без IDE и другого ассортимента инструментов), вы начинаете понимать, насколько мог помочь один исполняемый файл jar. 
Хорошо, я понимаю. Но в чем дело? Мы можем сделать такой пакет / zip / jar в один миг, если потребуется. Не правда ли? 

Во всей своей наивности я так и думал и выяснил ответ трудным путем.
Позволь мне провести тебя через это. Зажги своих редакторов. Давайте создадим исполняемый проект Jar. Я использую jdk1.7.0, STS, Maven 3.0.4. Если вы новичок в Maven или просто не в руки, я рекомендую вам прочитать
это и
это

Файл: C: \ projects \ MavenCommands.bat

ECHO OFF
REM =============================
REM Set the env. variables.
REM =============================
SET PATH=%PATH%;C:\ProgramFiles\apache-maven-3.0.4\bin;
SET JAVA_HOME=C:\ProgramFiles\Java\jdk1.7.0
REM =============================
REM Standalone java application.
REM =============================
call mvn archetype:generate ^
-DarchetypeArtifactId=maven-archetype-quickstart ^
-DinteractiveMode=false ^
-DgroupId=foo.bar ^
-DartifactId=executableJar001
pause


После запуска этого пакетного файла у вас будет полностью компилируемое стандартное Java-приложение.
Продолжайте, скомпилируйте его и соберите jar (mvn -e clean install). В итоге вы получите исполняемый файл Jar001-1.0-SNAPSHOT.jar по адресу C: \ projects \ executetableJar001 \ target. Теперь давайте пойдем »
java -jar jarFileName «. И вот ты споткнулся в первый раз. В словаре geeky он говорит вам, что не было класса с основным методом, и, следовательно, он не знал, что выполнять. 

К счастью, это легко.
Есть
стандартный процесс Java, чтобы решить это. И есть
плагин Maven, чтобы решить это. Я буду использовать последний. 

Обновленный файл: /executableJar001/pom.xml

...
 <dependencies>
 ...
 </dependencies>

 <build>
  <plugins>
   <!-- Set main class in the jar. -->
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-jar-plugin</artifactid>
    <version>2.4</version>
    <configuration>
     <archive>
      <manifest>
       <mainclass>foo.bar.App</mainclass>
      </manifest>
     </archive>
    </configuration>
   </plugin>

  </plugins>
 </build>
 ...



You can compile and assemble the application again (mvn -e clean install). It is will create a jar file in target folder. Try running the
jar from command line again. This time you will get intended result. So, we are all sorted, right?

Wrong. Very wrong.

Why? Everything seems fine. 
Let’s dig in a bit deeper and we will find why everything is not as sorted as it looks at the moment. Let’s go ahead and add a dependency e.g. let’s say we want to add logging and for that we want to use a third party jar i.e. logback. I will let Maven handle dependencies in the development environment. 
Updated File : /executableJar001/pom.xml

...
 <dependencies>
  <!-- Logging -->
  <dependency>
   <groupid>ch.qos.logback</groupid>
   <artifactid>logback-classic</artifactid>
   <version>1.0.9</version>
  </dependency>
 
 </dependencies>

 <build>
  ... 
 </build>



Updated File: /executableJar001/src/main/java/foo/bar/App.java

package foo.bar;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App 
{
 private final static Logger logger = LoggerFactory
   .getLogger(App.class);
 
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        logger.debug("Hello world from logger.");
    }
}

Now let’s compile and run the jar from command prompt using jar command. Did you see what happened?

    Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory  

Basically it is saying that the class (i.e the actual code) of the LoggerFactory (i.e. the 3rd party jar that we had added in development environment) was not found. 
Oh, but surely we should be able to tell java to pick up the 3rd party libraries from some folder.
Definitely. It is almost a certainty — if you are asking that question — that for most of your applications you tell the JVM where the 3rd party / dependency libraries are. You tell this by setting classpath. You could possibly be using some application server e.g. Tomcat / Jetty and that could be picking up some dependencies itself. And that is exactly where the problem originates. 
As a developer, I provide a x.jar that works. However, for it to work, it depends on a.jar (which in turn might depend upon b.jar and c.jar … you get the point). When I, as a developer, bundle up my deliverable, a x.jar, there is a dependency — on whoever I am handing this out to — to make sure that the classpath is correctly set in the other environment where x.jar is supposed to work. 
It is not that big a deal, mostly. However, it is not trivial either. There are multitude of ways that the dependencies on target environment could get messed up. There might be routine updates. There might be some other application deployed in the same production box, that needed an update on a jar that nobody thought would impact yours. We can discuss and debate the multitude of ways that these kind of mishaps can be stopped, but bottom line is x.jar (the developers responsibility) has dependencies (that the developer do not directly control). And that leads to mishaps. 
Of course, if you add into this mix the whole lot of variables that comes in because of different versions, different application servers, etc etc. the existing solution of providing x.jar only, quickly starts looking very fragile. 
So, what do we do? 
Say thanks to 
Dr. P. Simon Tuffs. This gentleman explains how he catered to this problem in this
link. It is a good read, I recommend it. What I have explained in very laymen terms (and have barely scratched the surface), Simon takes a deep dive into the problem and how he solved it. Long story short,
he coded a solution and made it open source.  
I am not going to replay the same information again — read
his article, it is quite informative — but I will call out the salient point of his solution. 
  1. It allows folks to create a single jar that contains everything — your code, resources, dependencies, application server (potentially) — everything. 
  2. It allows the end use to run this entire humongous jar by the simple java -jar jarFileName command. 
  3. It allows developers to develop the same way they have been developing e.g. if it is a web application, the war file structure, remains same. So there are no changes in the development process. 
Fine. So how do we go about doing it? 
There are many places where it is detailed out.
The One-JAR website.
Ant with One-JAR
Maven with One-JAR
Let’s see it in action on our dummy code. Thankfully there is also
a Maven plugin for this. Sadly it is not in the Maven Central repository (Why? Folks why? You have put in 98% of work. Why be sluggish about the last 2%?). It comes with nice
usage instructions
Updated file: /executableJar001/pom.xml

...
 
 <dependencies>
  ... 
 </dependencies>

 <build>
  <plugins>
   
   ...

   <!-- If you wanted to bundle all this in one jar. -->
   <plugin>
    <groupid>org.dstovall</groupid>
    <artifactid>onejar-maven-plugin</artifactid>
    <version>1.4.4</version>
    <executions>
     <execution>
      <goals>
       <goal>one-jar</goal>
      </goals>
     </execution>
    </executions>
   </plugin>
  </plugins>
 </build>
 
 <!-- Required only if you are usng onejar plugin. -->
 <pluginrepositories>
  <pluginrepository>
   <id>onejar-maven-plugin.googlecode.com</id>
   <url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
  </pluginrepository>
 </pluginrepositories>




Now all you need to do is run mvn -e clean package. You will get, apart from the normal jar, a fat self sufficient jar as well. Go ahead, do the java -jar jarFileName from command prompt again. It should work. 
Hmm.. that sounds good. Why isn’t everybody going for this? And this One-JAR seems to be around since 2004. Why are we not seeing more players in this market? 

You know what they say about free lunches? There are none. While the concept is quite neat and very practical, it does not mean that every other player have decided to join in. So if your website «needs» to be hosted on one of the biggie paid application servers (I don’t know why you want to keep paying for those proprietary software and folks that understand them. Should you not pay for only quality folks and rely on the open source apps that do not lock you in) One-JAR might not be a feasible solution for you. Also, I hear unconfirmed murmurs about how things might get sluggish (during load up  if your app is big. So, before you decide to commit to using this, I recommend you do a POC and make sure that other bits of your techstack are not unhappy with One-JAR.

My personal opinion is, 2004 was perhaps a little too early for this kind of thing. People were still struggling with stuff like standardization of  build and release process, getting clear player in ORM area, closing on a clear player for MVC framework etc. Not that those questions have been answered yet, or will be any-time soon. But I think the flavour of current problems in IT world are around 
  1. How to make DevOps work. 
  2. How to make the entire build and release automated. 
  3. How to leverage the open source libraries to provide solid dependable software while ensuring there are no heavy proprietary software causing lock-in and hence make the solution less agile for future business requirement. 

And in my mind, One-JAR plays very nicely in that area. So, I definitely expect to see more of this tool and / or more tools around this concept.

And, to be fair, there are more players in this area.
Dropwizard, although not the same thing, but in essence is very similar. They have extended the whole one jar concept with embedded app server, out of the box support for REST, JSON, Logback, sort of in a nice neat package, that  you could just use straight off the shelf. 
So, as I keep saying, these are nice exciting times to be in technology business, particularly if you like tinkering around with software. 

Hope you enjoyed reading this article. Please leave a comment if you have any suggestions and / or feedback. If you want to get in touch, you can look me up at 
Linkedin or 
Google+ .