Monday, April 18, 2011

Packaging and Distributing Java Desktop Applications

Creating Executable JAR File

This part of the tutorial shows how you can create a distributable application in the IDE and then run that application from outside of the IDE. We will package the application in the form of an executable JAR file.

A JAR file is an archive file that can contain multiple files and folders. JAR files are similar to zip files, but JAR files can have additional attributes that are useful for distributing Java applications. These attributes include digitally signing JAR files, additional compression, multiplatform compatibility, etc.

In this exercise, you create an IDE project and then place two pre-written Java source files into that project. Then you will compile the classes and build an executable JAR file. Afterwards, you will learn how to run the JAR file from outside of the IDE.

The classes used in this tutorial implement features of the GNU grep utility, which can be used for searching text or regular expression patterns inside text files. The project contains both command-line and GUI versions of the application, so that you can see different ways of running the application.

Creating a Project with Existing Sources

  1. Download the DeploymentTutorial.zip file and extract its contents on your system. 
    This zip archive contains source files for the application plus a few other files that will be used in the tutorial.

  2. In NetBeans IDE, choose File > New Project.

  3. In the Choose Category page, select Java Project With Existing Sources in the Java category and click Next.
  4. On the Name and Location page of the wizard, type AnotherGrep as the project name and specify the project's location. 
    Leave the Set as Main Project checkbox selected and click Next.

    The project folder does not have to be in the same location as the source files that you are importing into the project.

  5. On the Existing Sources page of the wizard, specify the sources that will be in the project. 
    Click the Add Folder button that is to the right of the Source Package Folders field. Navigate to the DeploymentTutorial folder that you have just unzipped on your system, expand the folder, select the src folder, and click Open. The src folder is added to your Source Package Folders field.
  6. Click Finish.

    Note: If, for example, you want to exclude some source files from importing into the project, click Next to open the last Includes & Excludes window. In our case, we want to use all the source files in the src folder, so we click Finish to finish working in the New Project wizard.

The project opens in the IDE and becomes visibile in the Projects window. You can explore the contents of the project by expanding the project's Source Packages node, where you should see classes called Grep and xGrep. Grep.java is a console version of the application. xGrep.java is a GUI version of the application and uses methods defined inGrep.java.

Configuring the Project

There are a few configuration steps you need to do, such as:

  • Choose the Java platform that will be used to compile the sources.
  • Set the project's main class. By doing this, you ensure that the JAR file that you create when you build the project is executable.

Verifying the Java Platform

Our project needs to be compiled and run on Java 6 platform. Therefore, you need to make sure that Java 6 is used as the platform for this project.

  1. Right-click the project's node and choose Properties.
  2. On the Libraries tab, ensure that the Java Platform is JDK 6.
  3. On the Sources tab, choose JDK 6 in the Source/Binary format.
  4. Click OK to close the Properties window.

Setting the Main Class

In order for a user to easily run your JAR file (by double-clicking the JAR file or by typing java -jar AnotherGrep.jar at the command line), a main class has to be specified inside the JAR's manifest file. (The manifest is a standard part of the JAR file that contains information about the JAR file that is useful for the java launcher when you want to run the application.) The main class serves as an entry point from which the java launcher runs your application.

When you build a project, the IDE builds the JAR file and includes a manifest. When you set the project's main class, you ensure that the main class is be designated in the manifest.

To set the project's main class:

  1. Right-click the project's node and choose Properties.
  2. Select the Run panel and enter anothergrep.xGrep in the Main Class field.
  3. Click OK to close the Project Properties dialog box.

When you build the project later in this tutorial, the manifest will be generated and include the following entry:

 Main-Class: anothergrep.xGRep

Building the Project and Creating the JAR File

Now that you have your sources ready and your project configured, it is time to build your project.

To build the project:

  • Choose Run > Build Main Project.
    Alternatively, right-click the project's node in the Projects window and choose Build.

When you build your project:

  • build and dist folders are added to your project folder (hereafter referred to as the PROJECT_HOME folder).
  • All of the sources are compiled into .class files, which are placed into the PROJECT_HOME/build folder.
  • A JAR file containing your project is created inside the PROJECT_HOME/dist folder.
  • If you have specified any libraries for the project (in addition to the JDK), a lib folder is created in the dist folder. The libraries are copied into dist/lib.
  • The manifest file in the JAR is updated to include entries that designate main class and any libraries that are on the project's classpath.

Note: You can view the contents of the manifest in the IDE's Files window. After you have built your project, switch to the Files window and navigate todist/AnotherGrep.jar. Expand the node for the JAR file, expand the META-INF folder, and double-click MANIFEST.MF to display the manifest in the Source Editor.

 Main-Class: anothergrep.xGrep
(To find more about manifest files, you can read this chapter from the Java Tutorial.)

Running and Distributing the JAR File

Running the Application Inside of the IDE

When developing applications in the IDE, typically you will need to test and refine them before distributing. You can easily test an application that you are working on by running the application from the IDE.

To run the AnotherGrep project in the IDE, right-click the project's node in the Projects window and choose Run.

The xGrep window should open. You can click the Browse button to choose a file in which to search for a text pattern. In the Search Pattern field, type text or a regular expression pattern that you would like to match, and click Search. The results of each match will appear in the xGrep window's Output area.

Information on regular expressions that you can use in this application are available here and in many other places.

Running the Application Outside of the IDE

Once you have finished developing the application and before you distribute it, you will probably want to make sure that the application also works outside of the IDE.

You can run the application outside of the IDE by following these steps:

  • In your system's file manager (for example, in the My Computer window on Windows XP systems), navigate to PROJECT_HOME/dist and double-click the AnotherGrep.jarfile.

You will know that the application has started successfully when the xGrep window opens.

If the xGrep window does not open, your system probably does not have a file association between JAR files and the Java Runtime Environment. See Troubleshooting JAR File Associations below.

Distributing the Application to Other Users

Now that you have verified that the application works outside of the IDE, you are ready to distribute it.

  • Send the application's JAR file to the people who will use the application. The users of your application should be able to run it by double-clicking the JAR file. If this does not work for them, show them the information in the Troubleshooting JAR File Associations section below.

Note: If your application depends on additional libraries other than those included in JDK, you need to also include them in your distribution (not the case in our example). The relative paths to these libraries are added in the classpath entry of the JAR's manifest file when you are developing your applicaiton in the IDE. If these additional libraries will not be found at the specified classpath (i.e., relative path) at launch, the application will not start. 
Create a zip archive that contains the application JAR file and the library and provide this zip file to users. Instruct the users to unpack the zip file making sure that the JAR file and libraries JAR files are in the same folder. Run the application JAR file.

Starting Your Java Application

The goal of this exercise is to show you some ways that you can start your application from the command line.

This exercise shows you how you can start a Java application in the following two ways:

  • Running the java command from the command line.
  • Using a script to a call a class in the JAR file.

Launching Applications From the Command Line

You can launch an application from the command line by using the java command. If you want to run an executable JAR file, use the -jar option of the command.

For example, to run the AnotherGrep application, you would take the following steps:

  1. Open a terminal window. On Microsoft Windows systems, you do this by choosing Start > Run, typing cmd in the Open field, and clicking OK.
  2. Change directories to the PROJECT_HOME/dist folder (using the cd command).
  3. Type the following line to run the application's main class:
     java -jar AnotherGrep.jar

If you follow these steps and the application does not run, you probably need to do one of the following things:

  • Include the full path to the java binary in the third step of the procedure. For example, you would type something like the following, depending on where your JDK or JRE is located:
     C:\Program Files\Java\jdk1.6.0_23\bin\java -jar AnotherGrep.jar
  • Add the Java binaries to your PATH environment variable, so that you never have to specify the path to the java binary from the command line. See Setting the PATH Environment Variable.

Launching Applications From a Script

If the application that you want to distribute is a console application, you might find that it is convenient to start the application from a a script, particularly if the application takes long and complex arguments to run. In this section, you will use a console version of the Grep program, where you need to pass the arguments (search pattern and file list) to the JAR file, which will be invoked in our script. To reduce typing at the command line, you will use a simple script suitable to run the test application.

First you need to change the main class in the application to be the console version of the class and rebuild the JAR file:

  1. In the IDE's Projects window, right-click the project's node (AnotherGrep) and choose Properties.
  2. Select the Run node and change the Main Class property to anothergrep.Grep (from anothergrep.xGrep). Click OK to close the Project Properties window.
  3. Right-click the project's node again and choose Clean and Build Project.

After completing these steps, the JAR file is rebuilt, and the Main-Class attribute of the JAR file's manifest is changed to point to anothergrep.Grep.

Java Web Start

Java Web Start (JWS) and the underlying Java Network Launch Protocol (JNLP) enable Java application delivery from a standard Web server. The end user initiates application installation by clicking on an URL. If the Java Web Start engine is not present on the system, the user is prompted to download and install it. Once Java Web Start is in place, clicking on the same URL will initiate the application download and installation procedures. It may involve download and installation of the required version of the JRE and Optional Packages. Upon their successful completion, the application is launched. The application will be cached on the user's system so next time the user clicks on the same URL, the JWS engine will launch the local copy of the application from the cache, if it detects that the computer is offline or the application was not updated on the Web site.

Another important feature of JWS is its ability to run your application in asandbox - a restricted container based on Java security architecture. But, unlike an applet, your application can gain access to local system resources like the filesystem, printer and system clipboard using the JNLP API even if it comes from an untrusted environment, after prompting the user for confirmation.

Java Web Start is available for Windows, Linux, and Solaris, and is part of MacOS X since v10.1. There are also third-party implementations of the JNLP protocol, some of them also include tools that assist you in the creation and maintenance of JNLP packages.

That was the bright side. Now, what is not so good about JNLP? First off, for seamless operation both the browser and the Web server that hosts the JNLP-enabled application must support application/x-java-jnlp-file MIME type. Some hosting providers do not support it. Moreover, versioning and incremental updates require additional support from the Web server, which has to be implemented using servlets, cgi-bin scripts, etc.

On the client side, a major browser would be configured to recognize the above MIME type during installation of the JWS engine, but users of less popular browsers, such as Opera, may have to do that manually.

JNLP-enabling an application may involve minor changes in its code and (re)packaging it into a set of jar files.

Before J2SE 5.0, JWS had very little to offer in terms of desktop integration - all it could do was create a desktop icon and/or a Start Menu entry for the application. On Windows, the application will not show up in Add/Remove Programs, so end users would have to run the Java Web Start application manager in order to remove your application.

Finally, JWS user interface needs much polishing. As of J2SE 5.0, users still complain about ugly windows with incomprehensible messages.

To sum it up, JWS can be a viable option in a controlled environment, such as corporate intranet, but it is not ready for the consumer market, where you may be better off using

Advanced Debugging

Skip all breakpoints

If you want to temporary de-activate all your breakpoints you can press the button "Skip all breakpoints" which is visible if you select the tab breakpoints.

If you press this button again the breakpoints will get activated again.

4.2. Breakpoint Properties

After setting a breakpoint you can select the properties of the breakpoint to for example use a condition to restrict when the breakpoint should get toggeled. In the properties you can for example restrict that the breakpoint should only be executed the 12 hit (Hit Count) or you can put in a conditional expression (which you can also use for logging if you want).

4.3. Watchpoint

A watchpoint is a breakpoint at which is stop whenever a field read or changed. You can set a watchpoint through a double-click on the left side before the field declaration. Via the properties of the watchpoint you can define if the breakpoint should be hit during read access (Field Access) or during write access (Field Modification).

4.4. Exception Breakpoint

The application is stopped if the specified exception is thrown. To define an exception breakpoint click on the following icon.

You can define if you want to stop and caught and / or uncaught exceptions.

4.5. Method Breakpoint

A method breakpoint is defined via double-click in the left border of the editor and the method head. You can define if you want to stop the program during method entry of after leaving the method.

4.6. Class breakpoint

A Class Load Breakpoint will stop when the class is loaded. Right-click on a class in the Outline View and choose "Toggle Class Load Breakpoint"

4.7. Hit Count

For every breakpoint you can define via the properties the hit count. If you use the hit count then the application is stop then the breakpoint is reached the number of times defined in the hit count.

4.8. Drop to frame

Eclipse allows you to select any level (frame) in the call stack during debugging and set the JVM to restart to that point.

This allows you to rerun a part of your program. Be aware that variables which have been modified by the code which you reset remain modified. The drop to frame will return to the the code. Changes made by the code, e.g. to variables / databases will not be reset.

To use the feature select the level in your stack and press the highlighted button.

Add dynamic Java code to your application

JavaServer Pages (JSP) is a more flexible technology than servlets because it can respond to dynamic changes at runtime. Can you imagine a common Java class that has this dynamic capability too? It would be interesting if you could modify the implementation of a service without redeploying it and update your application on the fly.

The article explains how to write dynamic Java code. It discusses runtime source code compilation, class reloading, and the use of the Proxy design pattern to make modifications to a dynamic class transparent to its caller.

An example of dynamic Java code

Let's start with an example of dynamic Java code that illustrates what true dynamic code means and also provides some context for further discussions. Please find this example's complete source code in Resources.

The example is a simple Java application that depends on a service called Postman. The Postman service is described as a Java interface and contains only one method, deliverMessage():

 public interface Postman {     void deliverMessage(String msg); }  


A simple implementation of this service prints messages to the console. The implementation class is the dynamic code. This class,PostmanImpl, is just a normal Java class, except it deploys with its source code instead of its compiled binary code:

 public class PostmanImpl implements Postman {

private PrintStream output; public PostmanImpl() { output = System.out; } public void deliverMessage(String msg) { output.println("[Postman] " + msg); output.flush(); } }


The application that uses the Postman service appears below. In the main() method, an infinite loop reads string messages from the command line and delivers them through the Postman service:

 public class PostmanApp {

public static void main(String[] args) throws Exception { BufferedReader sysin = new BufferedReader(new InputStreamReader(System.in));

// Obtain a Postman instance Postman postman = getPostman();

while (true) { System.out.print("Enter a message: "); String msg = sysin.readLine(); postman.deliverMessage(msg); } }

private static Postman getPostman() { // Omit for now, will come back later } }


Execute the application, enter some messages, and you will see outputs in the console such as the following (you candownload the example and run it yourself):

 [DynaCode] Init class sample.PostmanImpl Enter a message: hello world [Postman] hello world Enter a message: what a nice day! [Postman] what a nice day! Enter a message:   


Everything is straightforward except for the first line, which indicates that the class PostmanImpl is compiled and loaded.

Now we are ready to see something dynamic. Without stopping the application, let's modify PostmanImpl's source code. The new implementation delivers all the messages to a text file, instead of the console:

 // MODIFIED VERSION public class PostmanImpl implements Postman {

private PrintStream output; // Start of modification public PostmanImpl() throws IOException { output = new PrintStream(new FileOutputStream("msg.txt")); } // End of modification

public void deliverMessage(String msg) { output.println("[Postman] " + msg);

output.flush(); } }


Shift back to the application and enter more messages. What will happen? Yes, the messages go to the text file now. Look at the console

JDK 7 Support in NetBeans IDE 7.0:

To enable JDK 7 support in the NetBeans IDE:

  1. Download the JDK 7 binary for your platform from this page.
    This is the latest promoted development build.
  2. Install JDK 7 on your system.
  3. In the IDE, choose Tools > Java Platforms from the main menu.
  4. Click Add Platform and specify the directory that contains the JDK (e.g. on Windows, this is the JDK installation directory, default is C:\Program Files\Java\jdk1.7.0). 
    The directory that contains the Java platform is marked with the Java Platform icon icon.
  5. In the Platform Name step, verify that the default locations of the Platform Sources zip file and API documentation are valid.
  6. Click Finish to close the Add Java Platform dialog box.
  7. Ensure JDK 1.7 is chosen in the Platforms list and click Close.

Configuring the Project to Use JDK 7

Once you have registered JDK 7 in the IDE, you need to configure your project to use this JDK for compilation, running, and debugging.

  1. Create a Java project. Choose File > New Project and select Java Application as the project type. Click Next.
  2. Type SwitchTest as the project name and specify its location.
  3. In the Files window, right-click the SwitchTest project's node and choose Properties > Libraries. On this tab, choose JDK 1.7 from the list of Java Platforms.
    Setting JDK 7 as the target format.
  4. Switch to the Sources tab of the Project Properties window and choose JDK 7 as the Source/Binary Format. 
    Specifying JDK 7 as the source format
  5. Click OK to save changes. Your project is set to recognize new JDK 7 language features.

Using New JDK 7 Language Constructs: Switch Statement

JDK 7 brings a number of new features and enhancements in different areas, including internationalization, I/O and networking, security, etc. The best way to illustrate the JDK 7 support by the IDE's Java Editor is to demonstrate a few language changes introduced by Project Coin.

One of these changes is a "String in a switch". In the previous versions of Java, the argument of switch had to be only of the following primitive data types: byte, short, char,int, or enum. Starting from JDK 7, you can use arguments of type String in the expression of a switch statement.

  1. Open SwitchTest.java and add the following code. This small sample displays RGB codes for several colors. 
    With JDK 7, the color variable can be a String.
     package switchtest;      public class SwitchTest {      public static void main(String[] args) {          String color = "red";         String colorRGB;         switch (color.toLowerCase()) {             case "black": colorRGB = "000000"; break;             case "red": colorRGB = "ff0000"; break;             case "green": colorRGB = "008000"; break;             case "blue": colorRGB = "0000ff"; break;             default: colorRGB = "Invalid color"; break;         }         System.out.println(colorRGB);         }     } 

    If the pasted code is formatted incorrectly in the editor, press Alt-Shift-F to reformat.

  2. In the Files window, right-click the project's node and choose Run. You will see the output of the application, which is the RGB code for the red color. 
    You can see that the build is successful and the application works when the target platform and source format is JDK 7. 
    Output of running the project.
  3. Let's rollback to using JDK 6 and test how the application is complied with the JDK 6 compiler. 
    In the Files window, right-click the project's node and choose Properties. On the Libraries tab, set the Java Platform to JDK 1.6 and on the Sources tab, set the Source Format option to JDK 6.
    You can immediately see that the JDK6 parser does not recognize the syntax. The compilation fails because of the incompatible variable type. 
    Output of running the project.
  4. Now, let's rewrite the code using the if-then-else statement instead of switch as shown in the picture. 
    With JDK 7 being the target platform, the IDE recognizes such cases and offers you to convert them to switch. 
    Click the hint and the if-then-else construct will be automatically converted to exactly the same switch that we had before. 
    Converting the if-then-else to switch
  5. Run the application with different values.

JDK 7 Support: More Examples

To demonstrate how the IDE's Java Editor recognizes and automatically fixes code to be compliant with the JDK 7 language spec, let's use a dummy code snippet, which is meaningless but contains all the major language improvements.

When walking through this dummy code snippet and applying editor hints, you will see the following examples of how to:

  • Take advantage of automatic type inference, when the Java compiler is able to infer the type of a generic instance without the need to explicitly specify it. The so-calleddiamond operator is used to flag the type inference case.
  • Use improved exception handling or multi-catch, when one catch block can be used for several types of exceptions. 
  • Use the new syntax of resource closure statements introduced by the Automatic Resource Management feature.
  1. Replace the previous application code in the same SwitchTest.java file with the following
     package switchtest;   import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List;   public class SwitchTest {       public static void main(String[] args) throws IOException {         List list = new ArrayList();         HashMap map = new HashMap();         HashMap map2 = new HashMap();         String a = "ehlo";          try {             throw new FileNotFoundException("adasdf");         } catch (FileNotFoundException fnfo) {             fnfo.printStackTrace();         } catch (IOException ioe) {             ioe.printStackTrace();         }          FileInputStream in = null;         try {             in = new FileInputStream("foo.txt");              int k;             while ((k = in.read()) != -1) {                 System.out.write(k);             }         } finally {             if (in != null) {                 in.close();             }         }     } }  
  2. Note that the IDE displays several hints of how you can optimize your code for the JDK 7 spec. Simply, click on each hint and select the suggested action. 
    IDE's hints on conversion
  3. Finally, after you accept all the suggestions, you should receive the JDK 7 compatible code that is similar to the one shown below.
    Converted code snippet

Convert Java to EXE

f you are sure you need a real EXE, go straight to AOT Compilers.

"How do I make an .EXE file from my Java application?", "Need help converting jar to exe", "Is it possible to create a Windows executable using Java?" --- these and similar questions are among the most popular topics on Java developer forums. Should you start such a topic today, you are likely to encounter the following three types of replies:

  • "You cannot"
  • "You should not, because that would kill the very purpose of Java"
  • "You can do that with third party software X and Y"

The truth is that there exist two completely different approaches to the creation of native executables from Java applications, addressing different sets of problems. Moreover, under certain conditions some of those problems may be solved without making an EXE. So the most correct way to reply to such a post would be a request for more information, namely what is the goal of conversion to EXE. And the most frequent answer would be

Debug

Setting Breakpoints

To set breakpoints right click in the small left column in your source code editor and select toggle breakpoint. Or you can double click on this place.

3.2. Starting the Debugger

You can debug your application with selecting your Java file which contains a main method, right click it and select Run -> Debug.

Tip

If you have not defined any breakpoints this will run your program as normal. To debug the program you need to define breakpoints.

If you start the debugger the first time Eclipse will asked you if you want to switch to the debug perspective. Answer "yes", you should then see a perspective similar to the following.

You can use F5 / F6, F7 and F8 to step through your coding.

Table 1. Debugging Key bindings

Command Description
F5 Goes to the next step in your program. If the next step is a method / function this command will jump into the associated code.
F6F6 will step over the call, e.g. it will call a method / function without entering the associated code.
F7 F7 will go to the caller of the method/ function. So this will leave the current code and go to the calling code.
F8Use F8 to go to the next breakpoint. If no further breakpoint is encountered then the program will normally run.

You can of course use the ui to debug. The following displays the keybindings for the debug buttons.

3.3. Stack

The current stack is displayed in the "Debug" view.

3.4. Variables

The view "Variables" displays fields and local variables from the current stack.

Use the menu to display static variables.

Via the menu you can also customize the displayed columns, e.g. you can show the acutual and the declared type.

Another nice feature is the the "New Detail Formater" in which you can define how a variable is displayed. For example the toString method in our counter shows something meaningless, e.g. "de.vogella.debug.first.Counter@587c94". Use right mouse click on the variable -> "New Details Formater"

Maintain the following code to get the output "0" (or whatever is field result holds at this point).

Sending Email From Your Application Using Java Mail

Now a Day's informing the Clients about the successful updation of data or sending other automated information form your Java Programs, either from your Servlets or from your Applications has become a requirement, more than a feature. Here we show how to use the Java Mail API to send a Mail. To Test Program all you need to have is a SMTP address (Which your ISP Provides). 

Before Using this Program, you need to have Javasoft's JavaMail class files which can be downloaded from here http://www.javasoft.com/products/javamail/index.html

You will also need the JavaBeansTM Activation Framework extension or JAF (javax.activation). It is available at http://java.sun.com/beans/glasgow/jaf.html.

import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;

public void postMail( String recipients[ ], String subject, String message , String from) throws MessagingException
{
    boolean debug = false;

     //Set the host smtp address
     Properties props = new Properties();
     props.put("mail.smtp.host", "smtp.jcom.net");

    // create some properties and get the default Session
    Session session = Session.getDefaultInstance(props, null);
    session.setDebug(debug);

    // create a message
    Message msg = new MimeMessage(session);

    // set the from and to address
    InternetAddress addressFrom = new InternetAddress(from);
    msg.setFrom(addressFrom);

    InternetAddress[] addressTo = new InternetAddress[recipients.length]; 
    for (int i = 0; i < recipients.length; i++)
    {
        addressTo[i] = new InternetAddress(recipients[i]);
    }
    msg.setRecipients(Message.RecipientType.TO, addressTo);
   

    // Optional : You can also set your custom headers in the Email if you Want
    msg.addHeader("MyHeaderName", "myHeaderValue");

    // Setting the Subject and Content Type
    msg.setSubject(subject);
    msg.setContent(message, "text/plain");
    Transport.send(msg);
}

To Send a Email, from your Program, just call the above method, With the following parameters,

String to -- Email Address of the Recipient

String subject  -- Email Subject

String message -- Content or Body of the Message

String from -- Your (Senders) Email Address

smtp.jcom.net -- Replace this with your ISP's SMTP address.

That's it, you are all set to send a Email.