Running Tomcat with a security manager

Create a web application that contains the following jsp:

<html>
<body>
Trying to shutdown Tomcat, please press reload.
<%
   System.exit(1);
%>
</body>
</html>

Run Catalina (eg. catalina run) and load up the jsp. Notice in the Tomcat console that Tomcat has exited. What happens is that, by default, Tomcat is started without a security manager. The JSP, that was compiled into a servlet, runs in the same Virtual Machine as Tomcat itself, and System.exit causes the currently running VM to exit.

To prevent this from happening, run Tomcat with a Security Manager to not permit web applications to perform these kinds of operations. The security policy file used by Catalina is catalina.policy located in the [TOMCAT-HOME]/conf directory.

If you start Catalina again with the option -security (eg. catalina run -security, or startup -security), catalina.policy is taken into account.

If you then load the jsp, you would get the following error message in your browser window:

java.security.AccessControlException: access denied (java.lang.RuntimePermission exitVM)
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:272)
	at java.security.AccessController.checkPermission(AccessController.java:399)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:545)
	at java.lang.SecurityManager.checkExit(SecurityManager.java:765)
	at java.lang.Runtime.exit(Runtime.java:91)
	at java.lang.System.exit(System.java:701)
	at org.apache.jsp.ExitTomcat$jsp._jspService(ExitTomcat$jsp.java:59)
	at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:107)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
. . .

Using a ToggleButton in SWT

Main.java:

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
 
public class Main {
   public static void main(String[] args) {
      Display display = new Display();
      Shell shell = new Shell(display);
 
      // pos x, pos y, width, height
      shell.setBounds(200, 200, 300, 200);
      shell.setText("SWT ToggleButton Demonstration");
      shell.setLayout(new GridLayout());
 
      Group buttonGroup = new Group(shell, SWT.NONE);
      GridLayout gridLayout = new GridLayout();
      gridLayout.numColumns = 3;
      buttonGroup.setLayout(gridLayout);
      buttonGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 
      SelectionListener selectionListener = new SelectionAdapter () {
         public void widgetSelected(SelectionEvent event) {
            Button button = ((Button) event.widget);
            System.out.print(button.getText());
            System.out.println(" selected = " + button.getSelection());
         };
      };
      
      Button button1 = new Button(buttonGroup, SWT.TOGGLE);
      button1.setText("orange");
      button1.addSelectionListener(selectionListener);
      Button button2 = new Button(buttonGroup, SWT.TOGGLE);
      button2.setText("pear");
      button2.addSelectionListener(selectionListener);
      Button button3 = new Button(buttonGroup, SWT.TOGGLE);
      button3.setText("apple");
      button3.addSelectionListener(selectionListener);
      
      shell.open();
 
      while (!shell.isDisposed()) {
         if (!display.readAndDispatch()) {
            display.sleep();
         }
      }
      display.dispose();
   }
}

Creating an SWT PrintDialog

Main.java:

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.printing.PrintDialog;
import org.eclipse.swt.printing.PrinterData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
 
public class Main { 
   static Display display;
   static Shell shell;
   static Color color;
    
   public static void main(String[] args) {
      display = new Display();
      shell = new Shell(display);
 
      // pos x, pos y, width, height
      shell.setBounds(200, 200, 400, 200);
      shell.setText("SWT PrintDialog Demonstration");
      shell.setLayout(new GridLayout());
 
      final Group buttonGroup = new Group(shell, SWT.NONE);
      GridLayout gridLayout = new GridLayout();
      gridLayout.numColumns = 3;
      buttonGroup.setLayout(gridLayout);
      buttonGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
       
      final Button directoryDialogButton = new Button(buttonGroup, SWT.PUSH);
      directoryDialogButton.setText("Open PrintDialog");
 
      SelectionListener selectionListener = new SelectionAdapter () {
         public void widgetSelected(SelectionEvent event) {
            PrintDialog dialog = new PrintDialog (shell, SWT.MODELESS);
            dialog.setText("Print");
            PrinterData result = dialog.open ();
         };
      };
       
      directoryDialogButton.addSelectionListener(selectionListener);
       
      shell.open();
 
      while (!shell.isDisposed()) {
         if (!display.readAndDispatch()) {
            display.sleep();
         }
      }
      if (color != null && !color.isDisposed()) {
         color.dispose();
      }
      display.dispose();
   }
}

Creating an SWT Tree

Main.java:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
 
public class Main {
   public static void main(String[] args) {
      Display display = new Display();
      Shell shell = new Shell(display);
 
      // pos x, pos y, width, height
      shell.setBounds(200, 200, 300, 200);
      shell.setText("SWT Tree Demonstration");
      shell.setLayout(new GridLayout());
 
      Tree tree = new Tree(shell, SWT.BORDER);
       
      TreeItem node1 = new TreeItem (tree, SWT.NULL);
      node1.setText("Java");
       
      TreeItem node2 = new TreeItem(node1, SWT.NULL);
      node2.setText("j2se");
      TreeItem node3 = new TreeItem(node1, SWT.NULL);
      node3.setText("j2ee");
      TreeItem node4 = new TreeItem(node1, SWT.NULL);
      node4.setText("j2me");
       
      TreeItem node5 = new TreeItem(node2, SWT.NULL);
      node5.setText("http://java.sun.com/j2se/");
 
      TreeItem node6 = new TreeItem(node3, SWT.NULL);
      node6.setText("http://java.sun.com/j2se/");
  
      TreeItem node7 = new TreeItem(node4, SWT.NULL);
      node7.setText("http://java.sun.com/j2se/");
       
      tree.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL));
             
      shell.open();
 
      while (!shell.isDisposed()) {
         if (!display.readAndDispatch()) {
            display.sleep();
         }
      }
       
      display.dispose();
   }
}

Monitoring specific AWT events

The following example monitors Window events and Key events and prints them out.

Main.java:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
class Main extends JFrame
{
   public Main() {
      Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
         public void eventDispatched(AWTEvent e) {
            System.out.println(e);
         }
      }, AWTEvent.KEY_EVENT_MASK | AWTEvent.WINDOW_EVENT_MASK );
   }
 
   public static void main(String[] args) {
      Main main = new Main();
      main.setDefaultCloseOperation( EXIT_ON_CLOSE );
      main.setSize(200, 200);
      main.setVisible(true);
   }
}

Logging strings or exceptions to the JSP engine

The HttpServlet class extends from the GenericServlet superclass. This latter has two useful log methods:

   log(String)
   log(String, Throwable)

You can use these methods to write messages to the logs of your JSP/Servlet engine (they implement it all differently).
For example, the following JSP

<%
   log("Check your log, eg catalina_log...");
   log("An exception", new Exception());
%>
Lines logged!

logged the following in my current log file catalina_log.2003-04-01.txt:

2003-04-01 17:45:47 jsp: Check your log, eg catalina_log...
2003-04-01 17:45:47 jsp: An exception
java.lang.Exception
        at org.apache.jsp.logtest$jsp._jspService(logtest$jsp.java:56)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:107)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
        at org.apache.jasper.servlet.JspServlet$JspServletWrapper.service(JspServlet.java:201)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:381)
. . .

Look at the Tomcat category or documentation to see how you can change the file you want to log to.

Changing the color of the lines between JTable header cells

Main.java:

import javax.swing.plaf.basic.*;
import javax.swing.border.*;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
 
public class Main extends JFrame 
{
   public Main() {
      String[] header = {"column #1","column #2","column #3", "column #4"};
      String[][] data = {{"1","2","3","4"},
                         {"5","6","7","8"}};
 
      JTable table = new JTable(data, header);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      getContentPane().add(new JScrollPane(table));
 
      MyHeaderRenderer renderer = new MyHeaderRenderer();
      System.out.println(table.getTableHeader().getDefaultRenderer());
      table.getTableHeader().setDefaultRenderer(renderer);
       
      setSize(300,200);
   } 

 
   public static void main( String args[] ) { 
      Main main = new Main();
      main.setVisible(true);
   }
}
 
class MyHeaderRenderer extends DefaultTableCellRenderer 
{
   private static LineBorder border = new LeftColorLineBorder((Color) UIManager.get("Button.focus"));
 
   public java.awt.Component getTableCellRendererComponent(JTable table, Object value,
                                 boolean selected, boolean focus, int row, int col) {
      Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
 


      setHorizontalAlignment(JLabel.CENTER);
      setBorder(border);
      return c;
   }
}
 
class LeftColorLineBorder extends LineBorder
{
   public LeftColorLineBorder(Color color) {
      super(color, 1, false);
   }
 
   public LeftColorLineBorder(Color color, int thickness) {
      super(color, thickness, false);
   }
 
   public LeftColorLineBorder(Color color, int thickness, boolean roundedCorners) {
      super(color, thickness, roundedCorners);
   }
   
   public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
      super.paintBorder(c, g, x, y, width, height);
      g.setColor(Color.black);
      g.fillRect(x, y, x+4, y+height);
      g.fillRect(x+width-4, y, x+width, y+height);
   }
}

Using the classes CacheRequest and CacheResponse

JDK 1.5 provides a framework into which an caching implementation can be plugged. A standard protocol handler can use the ResponseCache class to get documents from the cache. For example, if the flag useCaches is set on a URLConnection (true by default), an attempt is made to fetch the content from the cache, implementation specified by ResponseCache.

Here’s an example with implementations of ResponseCache, CacheResponse and CacheRequest:

Main.java:

import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.*;

public class Main
{
   public static void main(String []args) throws Exception {
      if (args.length != 1) {
         System.err.println("Usage: java Main <URL>");
         System.exit(0);
      }

      ResponseCache.setDefault(new MyResponseCache());

      // download first time
      System.out.println("Downloading " + args[0] + " a first time");
      String doc = download(args[0]);
      
      System.out.println("nnDownloading " + args[0] + " a second time");
      // download second time (automatically from cache!)
      doc = download(args[0]);
   }
   
   public static String download(String urlString) throws Exception {          
      URL url = new URL(urlString);
      URLConnection connection = url.openConnection();
 
      connection.setConnectTimeout(5000);
      connection.connect();

      BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
      String lineSeparator = System.getProperty("line.separator");
      StringBuffer sb = new StringBuffer();
      String line;
      while ((line = br.readLine()) != null) {
         sb.append(line);
         sb.append(lineSeparator);
      }
      
      return sb.toString();
   }
}

class MyResponseCache extends ResponseCache
{
   public CacheResponse get(URI uri, String requestMethod, Map<String,List<String>> requestHeaders) {
      String cacheFilename = createCacheFilename(uri);
      if (!new File(cacheFilename).exists()) {
         System.out.println(""+uri + " does not exist in cache.");
         return null;
      }
      
      System.out.println("Retrieving " + uri + " from cache.");
      return new MyCacheResponse(createCacheFilename(uri));
   }
   
   public CacheRequest put(URI uri, URLConnection conn) {  
      System.out.println("Storing " + uri + " in cache.");    
      return new MyCacheRequest(createCacheFilename(uri));      
   }
   
   private String createCacheFilename(URI uri) {
      String suri = uri.toString();
      
      String notAllowed = "\/:*?"<>|";
      StringBuffer result = new StringBuffer();
      for (int i=0; i<suri.toString().length(); i++) {
         if (notAllowed.indexOf(suri.charAt(i)) == -1) {
            result.append(suri.charAt(i));
         }
      }
      return result.toString();   
   }
}

class MyCacheResponse extends CacheResponse
{
   private String filename;

   public MyCacheResponse(String filename) {
      this.filename = filename;
   }

   public Map<String, List<String>> getHeaders() throws IOException {
      return new HashMap<String, List<String>>();
   }

   public ReadableByteChannel getBody() throws IOException {
      return Channels.newChannel(new BufferedInputStream(new FileInputStream(filename)));
   }
}

class MyCacheRequest extends CacheRequest
{
   private String filename;
     
   public MyCacheRequest(String filename) {
      this.filename = filename;
   }
   
   public WritableByteChannel getBody() throws IOException {
      return Channels.newChannel(new BufferedOutputStream(new FileOutputStream(filename)));
   }
   
   public void abort() {
   }
}

Get started with an ArrayBlockingQueue

An ArrayBlockingQueue is a FIFO blocking queue with an array used by the underlying implementation. At construction, you specify the maximum space used by the queue. Attempting to put more elements in the queue than is allowed by its capacity constraint will result in a blocking wait. Similarly, attempting to take an element from an empty queue will block.

The following example starts up two threads, a consumer and a producer, that communicate with each other with an ArrayBlockingQueue and generic Message objects. The capacity is 5 elements. The producer tries to put 10 elements on the queue, but blocks until space in the queue is freed up by the consumer.

Main.java:

import java.util.concurrent.*;
 
public class Main
{
   public static void main(String []args) {
      BlockingQueue blockingQueue = new ArrayBlockingQueue<Message>(5);
       
      Producer p = new Producer(blockingQueue);
      new Thread(p).start();
 
      // wait a bit before consuming to allow the queue to fill up
      // and force a blocking wait
      try { Thread.sleep(1000); } catch(InterruptedException e) { }
      Consumer c = new Consumer(blockingQueue);
      new Thread(c).start();
   }
}
 
class Producer implements Runnable
{
   private BlockingQueue<Message> blockingQueue;
 
   public Producer(BlockingQueue<Message> blockingQueue) {
      this.blockingQueue = blockingQueue;
   }
 
   public void run() {
      for (int i=0; i<10; i++) {
         Message<String> m = new Message<String>("message contents #" + i);
         System.out.println("Producing '" + m + "'");
         try {
            blockingQueue.put(m);
         }
         catch(InterruptedException e) {
            return;
         }  
      }
   }
}
 
class Consumer implements Runnable
{
   private BlockingQueue<Message> blockingQueue;
   
   public Consumer(BlockingQueue<Message> blockingQueue) {
      this.blockingQueue = blockingQueue;
   }
    
   public void run() {
    
      while (true) {
         try {
            Message m = blockingQueue.take();
            System.out.println("tConsuming '" + m + "'");
         }
         catch(InterruptedException e) { } 
      }
   }
}
 
class Message<T> {
   private T contents;
    
   public Message(T contents) {
      this.contents = contents;
   }
    
   public T getContents() {
      return contents;
   }
    
   public String toString() {
      return ""+contents;
   }
}

outputs:

Producing 'message contents #0'
Producing 'message contents #1'
Producing 'message contents #2'
Producing 'message contents #3'
Producing 'message contents #4'
Producing 'message contents #5'
Producing 'message contents #6'
        Consuming 'message contents #0'
Producing 'message contents #7'
        Consuming 'message contents #1'
Producing 'message contents #8'
        Consuming 'message contents #2'
Producing 'message contents #9'
        Consuming 'message contents #3'
        Consuming 'message contents #4'
        Consuming 'message contents #5'
        Consuming 'message contents #6'
        Consuming 'message contents #7'
        Consuming 'message contents #8'
        Consuming 'message contents #9'

Notice that the following warnings are generated when compiling the above code:

Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

The reason for this is that the ArrayBlockingQueue is cast to a BlockingQueue without a specified type parameter (Message). This means that type safety may be broken. In fact, we could simply insert the following line of code to produce a ClassCastException at runtime:

      BlockingQueue blockingQueue = new ArrayBlockingQueue<Message>(5);
 
      try { blockingQueue.put("hello"); } catch(InterruptedException e) { }

Solve this by specifying the type parameter:

      BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<Message>(5);

An example of the other methods of the ArrayBlockingQueue can be found here.