Timeout on a network connection

If you didn’t know that there is a method available, you’d probably think of using a timer thread that closes the connection when it times out. There is an easier way to handle network timeouts. You can have better
control over your socket communications using socket options. And, to make developer’s life easy, there is this useful option called SO_TIMEOUT.

import java.net.*;
import java.io.*;
 
public class Main
{
   public static void main(String args[]) throws Exception {
      if (args.length != 3) {
         System.err.println("Usage: java Main <URL> <PORT> <TIMEOUT>");
         System.exit(1);
      }
  
      Socket s = new Socket(args[0], Integer.parseInt(args[1]));
 
      // set a timeout (in milliseconds)
      s.setSoTimeout(Integer.parseInt(args[2]));
 
      BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
      DataOutputStream dos = new DataOutputStream(s.getOutputStream());
 
      // get the homepage (/) by sending a GET command to the HTTP port
      dos.writeBytes("GET / HTTP/1.0rnrn");
 
      // read response, when timeout occurs, InterruptedIOException is thrown
      try {
         String line;
         while ((line = br.readLine()) != null) {
            System.out.println(line);
         }
      }
      catch(InterruptedIOException e) {
         System.out.println("Timed out after " + args[2] + " milliseconds.");
      }
      finally {
         br.close();
      }
   }
}

Now try to run it with different values for timeout:

C:> java Main www.yahoo.com 80 10
Timed out after 10 milliseconds.
 
C:> java Main www.esus.com 80 1000
 
[snip: yahoo home]

Pinging a host in Java

Why can’t I write ping in Java?
Ping requires ICMP packets. These packets can only be created via a socket of the SOCK_RAW type. Currently, Java only allows SOCK_STREAM (TCP) and SOCK_DGRAM (UDP) sockets. It seems unlikely that this will be added very soon, since many Unix versions only allow SOCK_RAW sockets to be created by root, and winsock does not address ICMP packets (win32 includes an unsupported and undocumented ICMP.DLL).

For a full discussion of socket types, see Stevens’ book (in the bibliography).

Rolling your own SecurityManager

If you look in the source code for FileOutputStream, you’ll notice that, before a file is created and written to, a check is performed to see whether it is allowed to do so.

java.io.FileOutputStream.java:

    public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
	    security.checkWrite(name);
	}
      . . .

If no SecurityManager is installed, System.getSecurityManager will return null and no security checks will be performed. We can see that happening by just creating an application that writes to a file.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

Running this code with java Main will write the file testfile.txt. If you run this code with java -Djava.security.manager Main, notice that an exception is thrown:

 Exception in thread "main" java.security.AccessControlException: access denied (
java.io.FilePermission testfile.txt write)
        at java.security.AccessControlContext.checkPermission(AccessControlConte
xt.java:195)
        at java.security.AccessController.checkPermission(AccessController.java:
403)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkWrite(SecurityManager.java:958)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:96)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:62)
        at Main.main(Main.java:5)

We could write our own SecurityManager that allows to write to testfile.txt and restricts everything else to the security sandbox.

MySecurityManager.java:

import java.io.*;
 
public class MySecurityManager extends SecurityManager {
   public void checkWrite(String file) {
      if (!file.equals("testfile.txt")) {
         throw new SecurityException("Only allowed to write to file testfile.txt.");
      }
   }
}

Now we can use our new security manager by either specifying it at command line:

C:> java -Djava.security.manager=MySecurityManager Main
testfile.txt successfully written!

or by doing so programmatically:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      System.setSecurityManager(new MySecurityManager()); 
 
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

Running:

C:> java Main
testfile.txt successfully written!

A custom security manager can be useful in JDK1.1 applications. However, it becomes a real pain (lots of programming) when you want to specify security restrictions based on who is running the code or where the code is coming from.

From JDK1.2, you can use policy files to customize the security manager in a fine-grained fashion. Let’s use the original program:
Main.java:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

and write a policy file that allows writing to only the file testfile.txt:
mypolicy.txt

grant {
   permission java.io.FilePermission "testfile.txt", "write";
};

Run the code and specify the policy file to be used at runtime:

C:> java -Djava.security.policy=mypolicy.txt -Djava.security.manager Main
testfile.txt successfully written!

Install the free Bouncy Castle JCE Provider

Bouncy Castle is a free provider for JCE. These following steps will explain how to add this security provider.

  1. Download the latest release for your JDK version at http://www.bouncycastle.org/latest_releases.html
  2. Extract the zip file in your home directory
  3. BouncyCastle doens’t come with a JAR file. In order to add it to the bootclasspath, create one:
         c:jce-jdk12-107classes> jar cvf bouncycastle.jar *
         c:jce-jdk12-107classes> copy bouncycastle.jar c:jdk1.2.2jrelibext
    
  4. Go to your JAVA_HOME/jre/lib/security directory and edit the file java.security. Look at the configuration, in my case it says something like:
       security.provider.1=sun.security.provider.Sun
       security.provider.2=com.sun.crypto.provider.SunJCE
    

    Add the line security.provider.3=org.bouncycastle.jce.provider.BouncyCastleProvider

  5. Run one of the examples to see if it is working (eg. How do I crypt/decrypt a message with the Blowfish algorithm?)

Update:

This information is outdated. Now, you can download JAR versions for your JDK version.

For 1.3.1, I downloaded jce-jdk13-118.jar and bcprov-jdk13-118.jar and added them to my classpath (you can also put them in jdk1.3.1/jre/lib/ext and NOT add them to your classpath).

You can add a static provider to jdk1.3.1/jre/lib/security/java.security as described above or you can do this programmatically in your code:

...
   java.security.Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
   Security.addProvider(provider);

Encrypting/decrypting with DES/CBC/PKCS5Padding

Look at the other questions for more information about the CBC mode and PKCS5Padding. Essentially, a CBC needs a initialization vector (IV) that is basically the seed for encrypting the first block of plaintext. A byte array is filled up with random bytes. You need the same initialization vector for decrypting. You can transmit or save this IV along with your encrypted message. The secret symmetric encryption/decryption key in our case is “password”.

Main.java:

import javax.crypto.spec.*;
import java.security.*;
import javax.crypto.*;
 
public class Main
{
   static IvParameterSpec iv;
 
   public static void main(String []args) throws Exception {
      String toEncrypt = "The shorter you live, the longer you're dead!";
 
      System.out.println("Encrypting...");
      byte[] encrypted = encrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = decrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] encrypt(String toEncrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("DES");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
 
      // generate an initialization vector (IV)
      SecureRandom secureRandom = new SecureRandom();
      byte[] ivspec = new byte[cipher.getBlockSize()];
      secureRandom.nextBytes(ivspec);
      iv = new IvParameterSpec(ivspec);
 
      // initialize the cipher with the key and IV
      cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
 
      // enctypt!
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String decrypt(byte[] toDecrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("DES");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
      cipher.init(Cipher.DECRYPT_MODE, sk, iv);
      byte[] decrypted = cipher.doFinal(toDecrypt);
 
      return new String(decrypted);
   }
}

(tested with the BouncyCastle JCE provider, http://www.bouncycastle.org)

Downloading a secure page (HTTPS) with Java

If you try to get data from an HTTPS-enabled page, you will get a MalformedURLException, as shown in following example:

Main.java:

import java.net.*;
import java.io.*;

public class Main {
   public static void main(String[] args) throws Exception {
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;

      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }
      br.close();
   }
}

outputs:

C:>java Main
Exception in thread "main" java.net.MalformedURLException: unknown protocol: https
        at java.net.URL.<init>(URL.java:497)
        at java.net.URL.<init>(URL.java:364)
        at java.net.URL.<init>(URL.java:308)
        at Main.main(Main.java, Compiled Code)

This is because Sun has no implementation for the HTTPS protocol in their core libraries. However, they created an reference implementation called Java Secure Socket Extension (JSSE) available as seperate download at http://java.sun.com/products/jsse. Include jcert.jar, jnet.jar and jsse.jar in your classpath and add the following two lines of code to the previous program:

      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

Here’s the updated example that works:

import java.security.*;
import java.net.*;
import java.io.*;

public class Main {
   public static void main(String[] args) throws Exception {
      // create a new protocol handler
      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");

      // protocol handler uses this security provider
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;

      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }

      br.close();
   }
}

A lenient DateFormat

If DateFormat is not lenient, the Date supplied has to be a valid one. If it is lenient, you can work with dates that are out of range. Eg. Dec. 40th, 2000 will refer to the date Jan 9th, 2001.

Main.java:

import java.util.*;
import java.text.*;
    
public class Main {
   public static void main(String args[]) {
      try {
         Date date;
 
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
         date = df.parse("12/40/00");   
         System.out.println("default:t" + date);
 
         df.setLenient(true);
         date = df.parse("12/40/00");   
         System.out.println("lenient:t" + date);
 
         df.setLenient(false);
         date = df.parse("12/40/00");   
         System.out.println("not lenient:t" + date);
      }
      catch(ParseException e) {
         e.printStackTrace();
      }
   }
}

outputs:

default:        Tue Jan 09 00:00:00 CET 2001
lenient:        Tue Jan 09 00:00:00 CET 2001
java.text.ParseException: Unparseable date: "12/40/00"
        at java.text.DateFormat.parse(DateFormat.java:331)
        at Main.main(Main.java:18)

Getting the number of days between two Dates

You can create two date objects, get the number of milliseconds that have passed since Jan 1, 1970 for each of them, make the difference and divide that back by the number of milliseconds per day (= 3600sec/hr * 24hrs * 1000ms)

Main.java:

import java.util.*;
 
public class Main {
   public static void main(String []args) {
      Calendar date1 = Calendar.getInstance();
      date1.set(2001, 6, 4);
 
      Calendar date2 = Calendar.getInstance();
      date2.set(1972, 12, 20);
 
      long diff = (date1.getTime().getTime() - date2.getTime().getTime()) / (3600*24*1000);
      System.out.println("Difference in time: " + diff);
   }
} 

Setting the default Locale in Java

Use Locale.setDefault.

Main.java:

import java.util.*;
 
public class Main {
   public static void main(String []args) {
      // for default locale 
      Locale locale = Locale.getDefault();
      System.out.println(locale);  
 
      Locale.setDefault(new Locale("nl", "BE")); 
      locale = Locale.getDefault();
      System.out.println(locale);  
   }
}

outputs:

en_US
nl_BE