Thursday, May 15, 2014

5 Things You Might Not Know About Git

Git is a version control system that has gained a lot of popularity over the past few years. It started out as a custom-designed VCS (version control system) for the Linux kernel and has since ballooned in popularity, arguably thanks to Github, a free source code hosting site that is powered by Git. Below are five facts about Git that you might not know:

1. Stashing

This command saves all the uncommitted modifications you've made to your working copy, and then reverts your working copy back to its original state. This is useful if you want to switch branches, but your changes are in an incomplete state and you don't want to commit anything yet (git will not allow you to switch branches if you have uncommitted changes).

The command to run a stash operation is git stash. When you're ready to re-apply your stashed changes, run git stash apply.

You can also create multiple stashes. To see a list of all stashes, run git stash list. By default, when you run git stash apply, it will apply the stash at the top of the list. To apply a different stash, run git stash apply stash@{2} where stash@{2} is the name of the stash as shown in the stash list.

Also note that when you apply a stash, it will remain in the stash list. To delete a stash, run git stash drop. Or, you can run git stash pop to apply a stash and then delete it.

Stashes are only stored in your local repository. They cannot be pushed to remote repositories.

For more information, see: http://git-scm.com/book/en/Git-Tools-Stashing

2. Amending commits

With most other version control systems, if you forgot to include a file in a commit, you have to make a second commit. This is annoying because it makes the commit history longer than it should be. With Git, instead of making a second commit, you can "amend" the previous commit. This will merge your commit in with the previous one.

> git commit --amend

For more information, see: http://git-scm.com/book/en/Git-Basics-Undoing-Things

3. Git is a file system

At its core, Git is actually a key/value data store. The commands that you use on a daily basis, like push and commit, are tools that are built on top of the data store. To demonstrate, I'll show you how to add and retrieve files from a Git repository, without using any of the typical Git commands.

First, initialize an empty repository:

> git init
Initialized empty Git repository in /home/michael/git-blog/.git/

Next, add a file to the repository. This command will return a SHA-1 hash, which we will need to retrieve the file again.

> echo 'file data' | git hash-object -w --stdin 
987721052266a93a2509c3a8ac9e8c86341d0835

Then, retrieve the file like so:

> git cat-file -p 987721052266a93a2509c3a8ac9e8c86341d0835
file data

For more information, see: http://git-scm.com/book/en/Git-Internals-Git-Objects

4. Commit message templates

If you work in a corporate environment, your team might have a policy on how commit messages have to be formatted. Git allows you to define a file that contains the default commit message to use for all commits.

> git config --global commit.template path/to/commit-message.txt

For more information, see: http://git-scm.com/book/en/Customizing-Git-Git-Configuration

5. Ignoring files during export

Git includes a command that lets you easily generate an archive file (.zip, .tar, etc) of your project. By default, all files in your project are included, but there may be some files which you want to exclude. To do this, use the export-ignore attribute:

> echo "test/ export-ignore" > .gitattributes
> git add .gitattributes
> git commit -m "Added .gitattributes file."
> git archive -o latest.zip HEAD

For more information, see: http://git-scm.com/book/en/Customizing-Git-Git-Attributes

Thursday, May 1, 2014

Presenter First: An Overview

Writing GUI applications can be difficult. It's easier to understand the flow of a command-line program--you start at the top and go to the bottom. But the flow of a GUI application, with its listeners, event handlers, and callbacks, goes all over the place. Add database queries and network calls to the mix, and things get even more complicated.

Enter MVP

The Model-View-Presenter (MVP) pattern helps to manage this complexity. MVP belongs to that family of design patterns that separates the application data and logic from the way in which the information is displayed to the user. To summarize:

  • The Model is responsible for maintaining the application's raw data (typically by persisting it in a database).
  • The View is responsible for presenting the data to the user (for example, in the form of a webpage or dialog box).
  • And the Presenter is responsible for tying the model and view together. In MVP, the model and view know nothing of each other!

Presenter First

The idea with "Presenter First" is that, using the MVP pattern, you start by writing the presenter class before anything else. This forces you to think abstractly about how your dialog window is going to behave. And, in the process of writing the presenter, you naturally figure out what functionalities the model and view will need to support. So, writing the model and view becomes just a matter of implementing an interface.

Another benefit to Presenter First is that it allows you to unit test your dialog's application logic. This is because the model and view are represented as interfaces, which can be easily mocked-out in the unit tests.

To summarize, the three benefits of Presenter First are:

  1. By using MVP, the view is cleanly separated from the application data and logic. In other words, your JFrame and JDialog classes become truly "dumb"--they contain no database calls or application logic.
  2. In the process of writing the presenter, the APIs for the model and view are essentially written automatically.
  3. The application logic of your dialog is finally unit-testable!

Example

As an example, let's create a simple login dialog. This dialog will ask the user for a username and password. If the credentials are valid, then a session token will be returned and the dialog will close. The user can also choose to have the application remember his username and password.

We start by writing the presenter class.

LoginPresenter.java

import java.awt.event.*;

public class LoginPresenter{
    private final ILoginView view;
    private final ILoginModel model;

    public LoginPresenter(ILoginView view, ILoginModel model){
        this.view = view;
        this.model = model;

        //invoked when the user clicks "Login"
        view.addLoginListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent event){
                onLogin();
            }
        });

        //invoked when the user clicks "Cancel" or closes the window
        view.addCancelListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent event){
                onCancel();
            }
        });

        //populate the dialog with its initial data
        view.setUsername(model.getCachedUsername());
        view.setPassword(model.getCachedPassword());
        view.setRememberMe(model.getCachedRememberMe());

        //finally, display the dialog
        view.display();
    }

    private void onLogin(){
        //get the data that the user entered
        String username = view.getUsername();
        String password = view.getPassword();
        boolean rememberMe = view.getRememberMe();

        //send the network call to log the user in
        String session = model.login(username, password);

        if (session == null){
            //credentials were bad, so show an error dialog to the user
            view.onBadLogin();
            return;
        }

        //persist the login credentials if "remember me" is checked
        if (rememberMe){
           model.setCachedUsername(username);
           model.setCachedPassword(password);
        } else {
           model.setCachedUsername("");
           model.setCachedPassword("");
        }

        model.setCachedRememberMe(rememberMe);
        model.setSession(session);

        view.onSuccessfulLogin();
        view.close();
    }

    private void onCancel(){
        view.close();
    }
}

The constructor adds event handlers which will fire with the user presses the "Login" and "Cancel" buttons. Then, it populates the view with data from the model (in this case, the saved username and password). The "onLogin()" method contains logic which determines if the login was successful or not and acts accordingly.

Now that our presenter is written, we can write the model and view interfaces, which allows the presenter class to compile.

ILoginModel.java

public interface ILoginModel{
    String login(String username, String password);

    String getCachedUsername();
    void setCachedUsername(String username);
    String getCachedPassword();
    void setCachedPassword(String password);
    boolean getCachedRememberMe();
    void setCachedRememberMe(boolean rememberMe);
    String getSession();
    void setSession(String session);
}

ILoginView.java

import java.awt.event.*;

public interface ILoginView{
    void addLoginListener(ActionListener listener);
    void addCancelListener(ActionListener listener);

    String getUsername();
    void setUsername(String username);
    String getPassword();
    void setPassword(String password);
    boolean getRememberMe();
    void setRememberMe(boolean rememberMe);

    void onBadLogin();
    void onSuccessfulLogin();

    void display();
    void close();
}

Next, we write our tests! Using a stubbing framework like Mockito helps, but it's not required (you could always create your own test implementations of the model and view interfaces).

LoginPresenterTest.java

import java.awt.event.*;
import java.util.*;
import org.junit.*;
import org.mockito.invocation.*;
import org.mockito.stubbing.*;
import static org.mockito.Mockito.*;

public class LoginPresenterTest{
    @Test
    public void init(){
        ILoginView view = mock(ILoginView.class);

        ILoginModel model = mock(ILoginModel.class);
        when(model.getCachedUsername()).thenReturn("user");
        when(model.getCachedPassword()).thenReturn("password");
        when(model.getCachedRememberMe()).thenReturn(true);

        LoginPresenter presenter = new LoginPresenter(view, model);

        verify(view).addLoginListener(any(ActionListener.class));
        verify(view).addCancelListener(any(ActionListener.class));
        verify(view).setUsername("user");
        verify(view).setPassword("password");
        verify(view).setRememberMe(true);
        verify(view).display();
    }

    @Test
    public void bad_login(){
        ILoginView view = mock(ILoginView.class);
        when(view.getUsername()).thenReturn("user");
        when(view.getPassword()).thenReturn("password");
        ListenerAnswer loginAnswer = new ListenerAnswer();
        doAnswer(loginAnswer).when(view).addLoginListener(any(ActionListener.class));

        ILoginModel model = mock(ILoginModel.class);
        when(model.login("user", "password")).thenReturn(null); //"null" = bad login

        LoginPresenter presenter = new LoginPresenter(view, model);

        //click "login"
        loginAnswer.fire();

        verify(model, never()).setSession(anyString());
        verify(view, never()).onSuccessfulLogin();
        verify(view).onBadLogin();
        verify(view, never()).close();
    }

    @Test
    public void valid_login(){
        ILoginView view = mock(ILoginView.class);
        when(view.getUsername()).thenReturn("user");
        when(view.getPassword()).thenReturn("password");
        ListenerAnswer loginAnswer = new ListenerAnswer();
        doAnswer(loginAnswer).when(view).addLoginListener(any(ActionListener.class));

        ILoginModel model = mock(ILoginModel.class);
        when(model.login("user", "password")).thenReturn("abc123"); //non-null token = good login

        LoginPresenter presenter = new LoginPresenter(view, model);

        //click "login"
        loginAnswer.fire();

        verify(model).setSession("abc123");
        verify(view, never()).onBadLogin();
        verify(view).onSuccessfulLogin();
        verify(view).close();
    }

    @Test
    public void rememberMe_true(){
        ILoginView view = mock(ILoginView.class);
        when(view.getUsername()).thenReturn("user");
        when(view.getPassword()).thenReturn("password");
        when(view.getRememberMe()).thenReturn(true);
        ListenerAnswer loginAnswer = new ListenerAnswer();
        doAnswer(loginAnswer).when(view).addLoginListener(any(ActionListener.class));

        ILoginModel model = mock(ILoginModel.class);
        when(model.login("user", "password")).thenReturn("abc123");

        LoginPresenter presenter = new LoginPresenter(view, model);

        //click "login"
        loginAnswer.fire();

        verify(model).setCachedUsername("user");
        verify(model).setCachedPassword("password");
        verify(model).setCachedRememberMe(true);
    }

    @Test
    public void rememberMe_false(){
        ILoginView view = mock(ILoginView.class);
        when(view.getUsername()).thenReturn("user");
        when(view.getPassword()).thenReturn("password");
        when(view.getRememberMe()).thenReturn(false);
        ListenerAnswer loginAnswer = new ListenerAnswer();
        doAnswer(loginAnswer).when(view).addLoginListener(any(ActionListener.class));

        ILoginModel model = mock(ILoginModel.class);
        when(model.login("user", "password")).thenReturn("abc123");

        LoginPresenter presenter = new LoginPresenter(view, model);

        //click "login"
        loginAnswer.fire();

        verify(model).setCachedUsername("");
        verify(model).setCachedPassword("");
        verify(model).setCachedRememberMe(false);
    }

    @Test
    public void cancel(){
        ILoginView view = mock(ILoginView.class);
        ListenerAnswer cancelAnswer = new ListenerAnswer();
        doAnswer(cancelAnswer).when(view).addCancelListener(any(ActionListener.class));

        ILoginModel model = mock(ILoginModel.class);

        LoginPresenter presenter = new LoginPresenter(view, model);

        //click "cancel"
        cancelAnswer.fire();

        verify(model, never()).setSession(anyString());
        verify(view).close();
    }

    private class ListenerAnswer implements Answer<Object>{
        private final List<ActionListener> listeners = new ArrayList<ActionListener>();

        public Object answer(InvocationOnMock invocation) {
            ActionListener listener = (ActionListener)invocation.getArguments()[0];
            listeners.add(listener);
            return null;
        }

        public void fire(){
            for (ActionListener listener : listeners){
                listener.actionPerformed(null);
            }
        }
    }
}

Once our tests pass, we can write the real implementations of the model and view interfaces. Again, this is basically just a matter of creating a new class and having that class implement the interface.

LoginViewImpl.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import static javax.swing.SpringLayout.*;

public class LoginViewImpl extends JFrame implements ILoginView {
    private final JButton login, cancel;
    private final JTextField username;
    private final JPasswordField password;
    private final JCheckBox rememberMe;

    public LoginViewImpl() {
        setTitle("Login");
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

        login = new JButton("Login");
        cancel = new JButton("Cancel");
        username = new JTextField();
        password = new JPasswordField();
        rememberMe = new JCheckBox("Remember me");

        JLabel title = new JLabel("Please enter your login credentials.");
        JLabel usernameLabel = new JLabel("Username:");
        JLabel passwordLabel = new JLabel("Password:");

        ///////////////////////

        Container contentPane = getContentPane();
        SpringLayout layout = new SpringLayout();
        contentPane.setLayout(layout);

        contentPane.add(title);
        contentPane.add(usernameLabel);
        contentPane.add(username);
        contentPane.add(passwordLabel);
        contentPane.add(password);
        contentPane.add(rememberMe);
        contentPane.add(login);
        contentPane.add(cancel);

        layout.putConstraint(WEST, title, 5, WEST, contentPane);
        layout.putConstraint(NORTH, title, 5, NORTH, contentPane);

        layout.putConstraint(WEST, usernameLabel, 5, WEST, contentPane);
        layout.putConstraint(NORTH, usernameLabel, 10, SOUTH, title);
        layout.putConstraint(WEST, username, 10, EAST, usernameLabel);
        layout.putConstraint(NORTH, username, 0, NORTH, usernameLabel);
        layout.putConstraint(EAST, username, 100, WEST, username);

        layout.putConstraint(WEST, passwordLabel, 5, WEST, contentPane);
        layout.putConstraint(NORTH, passwordLabel, 5, SOUTH, usernameLabel);
        layout.putConstraint(WEST, password, 0, WEST, username);
        layout.putConstraint(NORTH, password, 0, NORTH, passwordLabel);
        layout.putConstraint(EAST, password, 100, WEST, password);

        layout.putConstraint(WEST, rememberMe, 5, WEST, contentPane);
        layout.putConstraint(NORTH, rememberMe, 5, SOUTH, passwordLabel);

        layout.putConstraint(WEST, login, 5, WEST, contentPane);
        layout.putConstraint(NORTH, login, 10, SOUTH, rememberMe);
        layout.putConstraint(WEST, cancel, 5, EAST, login);
        layout.putConstraint(NORTH, cancel, 0, NORTH, login);

        setSize(300,200);
        setLocationRelativeTo(null);
    }

    public void addLoginListener(ActionListener listener) {
        login.addActionListener(listener);
        username.addActionListener(listener);
        password.addActionListener(listener);
    }

    public void addCancelListener(final ActionListener listener) {
        cancel.addActionListener(listener);
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent event){
                listener.actionPerformed(null);
            }
        });
    }

    public String getUsername() {
        return username.getText();
    }

    public void setUsername(String username) {
        this.username.setText(username);
    }

    public String getPassword() {
        return new String(password.getPassword());
    }

    public void setPassword(String password){
        this.password.setText(password);
    }

    public boolean getRememberMe() {
        return rememberMe.isSelected();
    }

    public void setRememberMe(boolean rememberMe) {
        this.rememberMe.setSelected(rememberMe);
    }

    public void onBadLogin() {
        JOptionPane.showMessageDialog(this, "Invalid login credentials.");
    }

    public void onSuccessfulLogin() {
        JOptionPane.showMessageDialog(this, "Login successful.");
    }

    public void display() {
        setVisible(true);
    }

    public void close() {
        dispose();
    }
}

LoginModelImpl.java

import java.io.*;
import java.util.*;

public class LoginModelImpl implements ILoginModel{
    private final File file;
    private final Properties properties;
    private String session;

    public LoginModelImpl(File file) throws IOException{
        this.file = file;
        this.properties = new Properties();

        if (file.exists()){
            this.properties.load(new FileReader(file));
        }
    }

    public String login(String username, String password){
        //normally, a network or database call would be made here
        if ("test".equals(username) && "test".equals(password)){
            return "abc123";
        }
        return null;
    }

    public String getCachedUsername(){
        return properties.getProperty("username");
    }

    public void setCachedUsername(String username){
        properties.setProperty("username", username);
        save();
    }

    public String getCachedPassword(){
        return properties.getProperty("password");
    }

    public void setCachedPassword(String password){
        properties.setProperty("password", password);
        save();
    }

    public boolean getCachedRememberMe(){
        String value = properties.getProperty("rememberMe");
        return (value == null) ? false : Boolean.parseBoolean(value);
    }

    public void setCachedRememberMe(boolean rememberMe){
        properties.setProperty("rememberMe", rememberMe + "");
        save();
    }

    public String getSession(){
        return session;
    }

    public void setSession(String session){
        this.session = session;
    }

    private void save() {
        try {
            properties.store(new FileWriter(file), "");
        } catch (IOException e){
            throw new RuntimeException(e);
        }
    }
}

To run our program, we simply create a new instance of LoginPresenter, passing in the model and view implementations that we created above.

Main.java

import java.io.*;

public class Main{
    public static void main(String args[]) throws Throwable {
        File cache = new File("cache.properties");

        ILoginModel model = new LoginModelImpl(cache);
        ILoginView view = new LoginViewImpl();
        new LoginPresenter(view, model);
    }
}

And that's all there is to it!

Download the source code

References:

Saturday, September 7, 2013

Adding syntax highlighting to Javadocs

Often times, when writing Javadocs, it helps to include source code samples along with the documentation. Typically, this is achieved by inserting the source code into a <pre> tag. This will render the code in a monospaced font when viewed in a browser.

/**
 * <p>Represents a fruit.</p>
 * <pre>
 * //create a new fruit
 * Fruit fruit = new Fruit("banana");
 *
 * //copy an existing fruit
 * Fruit copy = new Fruit(fruit);
 * </pre>
 * @author John Doe
 */
public class Fruit{
  ...
}

But there tools out there that can add syntax highlighting to source code on a web page. Javadocs are a webpage. Why can't these syntax highlighting tools be applied to Javadocs as well?

In this blog post, I am going to show you how to add syntax highlighting to a Maven-enabled Java project. I will be using the popular Javascript-based SyntaxHighlighter library for the syntax highlighting.

1. Download SyntaxHighligher

First, download SyntaxHighlighter.

2. Create a CSS file

SyntaxHighlighter makes use of CSS styling to perform the code coloring. Luckily, the Javadoc tool allows you to specify a CSS file to customize the look and feel of your Javadoc webpage. So, we will need to create a CSS file that contains the styling that SyntaxHighlighter requires.

Navigate to the SyntaxHighlighter files that you downloaded in the previous step. In the "styles" directory, locate the "shCore.css" file and one of the "shTheme" files (such as "shThemeDefault.css", see: all the available themes). Combine these two files into a single file and give it a name of your choosing. Save this file somewhere within your project folder. The location doesn't matter, since Javadoc will end up copying the file when the Javadocs are generated. A good place is the "src/main/javadoc" folder, as this is the standard Maven location for all Javadoc-related resources.

3. Configure your POM file

Next, we will need to add some configuration settings to the project POM. In the configuration section of the "maven-javadoc-plugin" plugin, add the following: (1) the location of the CSS file that was created in the previous step, (2) <script> tags for the SyntaxHighlighter Javascript files, and (3) Javascript code to configure and initialize SyntaxHighlighter.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <version>2.8.1</version>
  <configuration>

    <!-- (1) CSS file location -->
    <stylesheetfile>src/main/javadoc/syntax-highlighter.css</stylesheetfile>

    <!-- (2) SyntaxHighlighter Javascript files -->
    <top><![CDATA[
      <script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
      <script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js" type="text/javascript"></script>
    ]]></top>

    <!--
    (3) SyntaxHighlighter configuration and initialization
    See: http://alexgorbatchev.com/SyntaxHighlighter/manual/configuration/
    -->
    <footer><![CDATA[
      <script type="text/javascript">
        SyntaxHighlighter.defaults["auto-links"] = false;
        SyntaxHighlighter.defaults["tab-size"] = 2;
        SyntaxHighlighter.all();
      </script>
    ]]></footer>

  </configuration>
</plugin>

A list of available SyntaxHighlighter configuration settings can be found on the SyntaxHighlighter homepage.

4. Modify the Javadocs

Each <pre> tag in the Javadocs must be given a class="brush:java" attribute. This signals to SyntaxHighlighter that the text content should be treated as Java source code.

/**
 * <p>Represents a fruit.</p>
 * <pre class="brush:java">
 * //create a new fruit
 * Fruit fruit = new Fruit("banana");
 *
 * //copy an existing fruit
 * Fruit copy = new Fruit(fruit);
 * </pre>
 * @author John Doe
 */
public class Fruit{
  ...
}

5. Generate the Javadocs

Instruct Maven to generate the Javadocs for the project by running the following command:

mvn javadoc:javadoc

And that's it! You should be good to go.

References:

Wednesday, July 17, 2013

ACM Webcast on Parallel Programming

I just finished listening to an ACM webcast entitled "Changing How Programmers Think about Parallel Programming". It was presented by William Gropp from the University of Illinois at Urbana-Champaign and it was very informative!

He discussed two different types of parallel programming. Course grained parallelism is where you divide a task up into chunks and each process performs the same sequence of operations on its assigned chunk. For example, say you have to mail some letters and have multiple people at your disposal to help you. With course grained parallelism, each person would be given some number of letters to mail and would be responsible for all tasks that are involved with mailing the letter, such as folding the paper, placing it in the envelope, and applying a stamp.

Fine grained parallelism, however, is where you divide the task up by operation and each process is assigned to a specific operation. Using the letter example, this would mean that one person would be responsible for folding the paper, another for placing the paper in the envelope, and so on.

He discussed the fact that processes must often share data with each other. One technique for doing this is for each process to copy some of the data that is assigned to other processes before execution begins. He also mentioned that parallel programs are harder to debug than traditional, single-threaded programs, which is one reason why programming in a parallel fashion is so difficult!

Thursday, June 20, 2013

Working with Timezones in Java

Timezones are confusing to say the least. Here are four things to keep in mind when working with timezones in Java.

1. Timezones are relative to UTC

UTC, which stands for "Coordinated Universal Time", is always the same no matter where on the planet you are. The term "GMT" is often used as well. While GMT is pretty much identical to UTC, it's not exactly the same. UTC is scientifically defined, while GMT is not.

A timezone is an offset from UTC. For example, my timezone is currently 4 hours behind UTC. However, a timezone's offset may change depending on the time of year. For example, my timezone is 4 hours behind UTC for half the year (daylight savings time) and 5 hours behind UTC for the other half of the year (standard time). This is why it's preferable to represent timezones using their IDs (such as "America/New_York", described in more detail below) instead of their offsets (such as "-0400"). An offset can change depending on the time of year, but a timezone ID encapsulates these offset variations.

Fun fact: You might have noticed that the letters in the acronym "UTC" don't match up with "Coordinated Universal Time". "UTC" actually arose as a compromise between the English version "CUT" (Coordinated Universal Time), and the French version "TUC" (Temps Universel Coordonné).

2. Date objects use UTC

Whenever you call the toString() method on a java.util.Date object (for example, by printing the object to the console), it will generate a string that looks something like this:

Thu Jun 20 13:23:52 EDT 2013

This might lead you to believe that this is the exact timestamp that the object is holding. Not necessarily. Internally, Date objects store their timestamps in UTC. When you call toString(), it converts the UTC timestamp to your JVM's default timezone.

3. Timezones are clothing

Think of timezones as just different sets of clothing a timestamp can wear. It can put on a sweater, a jacket, or a tuxedo, but underneath it all, it's still a UTC timestamp. To format a Date object using a timezone of your choosing, call the setTimeZone() method on the DateFormat class.

DateFormat formatter = new SimpleDateFormat("HH:mm Z");
TimeZone timezone = TimeZone.getTimeZone("Europe/Madrid");
formatter.setTimeZone(timezone);
System.out.println(formatter.format(new Date()));
//prints: 19:50 +0200

In the example above, I'm printing the current time in Madrid, Spain (and surrounding areas). The "+0200" part describes the timezone's offset from UTC at that moment in time. It shows that the timezone is 2 hours and 0 minutes ahead of UTC. To give another example of an offset, "-0430" would mean that the timezone is 4 hours and 30 minutes behind UTC.

You might be wondering where a list of these timezone identifier strings can be found. A source that I like to use is from the PHP user manual. But the official listing can be found in what's known as the TZ database.

4. The TimeZone class is quirky

Note that if an unrecognized timezone ID is passed into the TimeZone.getTimeZone() method, the method will return an object representing the "GMT" timezone. It would be less confusing if it just returned null, but who am I to complain.

TimeZone timezone = TimeZone.getTimeZone("Bogus/Timezone");
if ("GMT".equals(timezone.getID())){
  //timezone not found!
} else {
  //timezone found
}

Thursday, April 18, 2013

Blobs and JDBC

A blob is a database data type for storing raw, binary data. It stands for "binary large object". In this blog post, I'm going to show you how to use this data type to insert and retrieve a photo using JDBC.

To insert the photo, start by creating an InputStream object to the photo you want to insert. For example, if the photo resides in a file, create a FileInputStream object.

File file = new File("photo.jpg");
InputStream in = new FileInputStream(file);

Then, create a PreparedStatement object for your INSERT statement. The PreparedStatement should contain a parameter for where the binary data should go, just as if you were inserting "normal" data, like a string or an integer.

Connection conn = ...
PreparedStatement stmt = conn.prepareStatement("INSERT INTO test (photo) VALUES (?)");

To set the binary data, pass the InputStream object into the setBlob() method, and then execute the statement.

stmt.setBlob(1, in);
stmt.execute();

To retrieve blob data from the database, call the getBlob() method on the ResultSet object that is returned from the SELECT statement. This will return a Blob object. Then, invoke the Blob.getBinaryStream() method to get an InputStream to the binary data.

Connection conn = ...
PreparedStatement stmt = conn.prepareStatement("SELECT photo FROM test");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
  Blob blob = rs.getBlob(1);
  InputStream in = blob.getBinaryStream();
  ...
}

Tuesday, April 16, 2013

4 Ways to Initialize a List in Java

Creating a List and populating it with a set of elements is a common programming task in Java. In this blog post, I'm going to describe four ways to do this.

1. Collections.emptyList()

This method will return a List object that is empty. This is a convenient, shorthand alternative to explicitly instantiating a new List object. However, this list is immutable, which means that you cannot add any elements to it.

List<String> list = Collections.emptyList();

2. Arrays.asList()

This method takes an array and converts it to a List object. What makes this method special is the fact that the argument to this method is a vararg. This means that you can pass as many elements into the method as you like. The syntax is very compact because all of the elements can fit on one line.

But note that, just as with Collections.emptyList(), the list that is created is immutable, so you cannot add or remove elements to/from it.

List<String> list = Arrays.asList("one", "two", "three");

3. Anonymous child class

A somewhat trickier way of creating a list is to define your list as an anonymous, child class. The elements are added to the list by calling the add() method within the class' initializer block (notice the double braces).

List<String> list = new ArrayList<String>(){{add("one"); add("two"); add("three");}}

4. JUST CALL add(), EINSTEIN!

Of course, the traditional way to create a list is to instantiate it and then call the add() method for each element. But where's the fun in that?

List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");