November 6, 2011

How to set Wicket up with Tomcat

The Wicket quickstart project is great, but it can be confusing to progress from that embedded Jetty server to Tomcat (or any other more stable setup), especially if it's your first time setting up a web application. Here's how to make it happen using nothing but ant. (If you can't get the quickstart project working, see my earlier post first.)

The first step is making sure you have all your tools. You're going to need recent versions of both Tomcat and Apache ant already installed.

Just as important as your tools is your directory structure. There is some room for individuality here, but the structure I'm going to show you follows fairly standard conventions. I didn't include version numbers with the JAR files because they'll probably have changed by the time you read this anyways.

\WicketTomcat
  +---src
  | +---main
  | | +---java
  | | | \---com
  | | |   \---HelloWicket
  | | |         HelloWorld.java
  | | |         HelloWorld.html
  | | |         HelloWorldApplication.java
  | | \---webapp
  | |   \---WEB-INF
  | |         web.xml
  | \---test
  |   \---java
  +---lib
  |     junit.jar
  |     log4j.jar
  |     servlet-api.jar
  |     slf4j-api.jar
  |     slf4j-log4j.jar
  |     wicket.jar
  |     wicket-extensions.jar
  +---target
    build.xml

Now that you have all the pieces in place, all you have to do is fill in the files' contents. I happened to use Eclipse Indigo, but there is absolutely nothing IDE-specific about these instructions. You can use any IDE or text editor you want.

HelloWorld.java:

package com.HelloWicket;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;

public class HelloWorld extends WebPage {
public HelloWorld() {
add(new Label("message", "Hello, Wicket!"));
}
}


HelloWorld.html

<html>
<head>
<title>Wicket Tomcat test title</title>
</head>
<body>
<span wicket:id="message">Message goes here</span>
</body>
</html>


HelloWorldApplication.java

package com.HelloWicket;

import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;

public class HelloWorldApplication extends WebApplication {
public HelloWorldApplication() {
}

/**
* @see org.apache.wicket.Application#getHomePage()
*/
@Override
public Class getHomePage() {
return HelloWorld.class;
}
}


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Extremely simple example of deploying Wicket on Tomcat</display-name>
<context-param>
<param-name>configuration</param-name>
<param-value>development</param-value> <!-- Wicket mode (development or deployment) -->
</context-param>
<filter>
<filter-name>HelloWicket</filter-name> <!-- To be used in filter-mapping > filter-name below -->
<filter-class>
org.apache.wicket.protocol.http.WicketFilter
</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>
com.HelloWicket.HelloWorldApplication <!-- Fully qualified name of WebApplication class -->
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HelloWicket</filter-name> <!-- Must match filter > filter-name above -->
<url-pattern>/*</url-pattern> <!-- Take control of all URLs that start with http://localhost:8080/HelloWicket/ -->
</filter-mapping>
</web-app>
<!--
After deploying to Tomcat, access with http://localhost:8080/HelloWicket/.

Source: http://wicket.apache.org/learn/examples/helloworld.html
-->


build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project default="war" name="HelloWicket" basedir=".">
<property name="final.name" value="HelloWicket" />
<property name="src.main.dir" value="src/main/java" />
<property name="src.test.dir" value="src/test/java" />
<property name="src.web.dir" value="src/main/webapp" />
<property name="lib.dir" value="lib" />
<property name="build.dir" value="target" />
<property name="build.main.classes" value="${build.dir}/classes" />
<property name="build.test.classes" value="${build.dir}/test-classes" />
<property name="build.test.reports" value="${build.dir}/test-reports" />
<property name="build.reports.dir" value="${build.dir}/reports" />
<property name="tomcat.dir" value="..\..\..\..\Program Files\Apache Software Foundation\apache-tomcat-7.0.22\webapps" />

<path id="build.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</path>
<target name="clean">
<delete dir="${build.dir}" failonerror="false" />
<delete file="${final.name}.war" failonerror="false" />
</target>
<target name="init">
<mkdir dir="${build.dir}" />
</target>
<target name="compile" depends="init">
<mkdir dir="${build.main.classes}" />
<javac destdir="${build.main.classes}" target="1.6" source="1.6" srcdir="${src.main.dir}" classpathref="build.classpath" includeantruntime="false" />
<copy todir="${build.main.classes}">
<fileset dir="${src.main.dir}">
<include name="**/*.*" />
<exclude name="**/*.java" />
</fileset>
</copy>
</target>
<target name="test-compile" depends="compile">
<mkdir dir="${build.test.classes}" />
<javac destdir="${build.test.classes}" target="1.6" source="1.6" srcdir="${src.test.dir}" includeantruntime="false">
<classpath>
<path refid="build.classpath" />
<pathelement path="${build.main.classes}" />
</classpath>
</javac>
<copy todir="${build.test.classes}">
<fileset dir="${src.test.dir}">
<include name="**/*.*" />
<exclude name="**/*.java" />
</fileset>
</copy>
</target>
<target name="test" depends="test-compile">
<mkdir dir="${build.test.reports}" />
<junit dir="./" failureproperty="test.failure" printSummary="yes" fork="true" haltonerror="true">
<sysproperty key="basedir" value="." />
<formatter type="xml" />
<classpath>
<path refid="build.classpath" />
<pathelement path="${build.main.classes}" />
<pathelement path="${build.test.classes}" />
</classpath>
<batchtest todir="${build.test.reports}">
<fileset dir="${src.test.dir}">
<include name="**/*Test*.java" />
</fileset>
</batchtest>
</junit>
<mkdir dir="${build.reports.dir}" />
<junitreport todir="${build.reports.dir}">
<fileset dir="${build.test.reports}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${build.reports.dir}" />
</junitreport>
</target>
<target name="war" depends="test">
<war destfile="${build.dir}/${final.name}.war" webxml="${src.web.dir}/WEB-INF/web.xml">
<lib dir="lib">
<include name="wicket*.jar" />
<include name="slf4j*.jar" />
<include name="log4j*.jar" />
<include name="servlet*.jar" />
</lib>
<classes dir="${build.main.classes}" />
<fileset dir="${src.web.dir}">
<include name="**/*" />
<exclude name="**/web.xml" />
</fileset>
</war>
</target>

<target name="deploy" depends="war">
<echo>Deploying .war to local Tomcat</echo>
<copy todir="${tomcat.dir}">
<fileset dir="${build.dir}" includes="${final.name}.war" />
</copy>
</target>
</project>


Simply adjust for your local Tomcat install's webapps directory, and you should be all set!

Sources:
Chapter 15 of Wicket in Action, free to download from the book's website
The Wicket website's quickstart page and Hello World example page

February 10, 2011

Simple sample to create a dynamic flash card layout in Swing

  • Solid white background
  • Two dynamically resizing text fields
  • Buttons have equal width
  • Buttons float to the top, no more weird grid-based spacing issues
  • Text areas are scrollable
  • Starts out small, but window can be adjusted to any reasonable size, even maximized
package migLayoutCenterExpandTest;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import net.miginfocom.swing.MigLayout;

public class Main {
    static JFrame      frame;

    static JPanel      firstPanel;
    static JPanel      secondPanel;

    static JLabel      firstPanelLabel;
    static JButton     firstPanelButton;

    static JTextArea   textArea1;
    static JTextArea   textArea2;
    static JScrollPane textScrollPane1;
    static JScrollPane textScrollPane2;
    static JButton     button1;
    static JButton     button2;
    static JButton     button3;
    static JButton     button4;
    static JButton     button5;
    static JButton     button6;

    /**
     * @param args
     */
    public static void main(String[] args) {
        createObjects();

        setUpFrame();
        setUpFirstPanel();
        setUpSecondPanel();

        frame.getContentPane().add(firstPanel);
        frame.setVisible(true);
    }

    public static void createObjects() {
        frame = new JFrame();

        firstPanel = new JPanel();

        firstPanelLabel = new JLabel("first label");
        firstPanelButton = new JButton("change screens");

        secondPanel = new JPanel();

        textArea1 = new JTextArea("Text area 1");
        textArea2 = new JTextArea("Text area 2");
        textScrollPane1 = new JScrollPane();
        textScrollPane2 = new JScrollPane();
        button1 = new JButton("Button one text");
        button2 = new JButton("Button two text");
        button3 = new JButton("Button three text");
        button4 = new JButton("Button four text");
        button5 = new JButton("Button five text");
        button6 = new JButton("Button six text");
    }

    public static void setUpFrame() {
        frame.setSize(400, 300);

        JPanel contentPane = (JPanel) frame.getContentPane();
        contentPane.setLayout(new MigLayout("align 50% 50%, filly"));
        contentPane.setBackground(Color.WHITE); // Disable this to debug panel sizes

        frame.addWindowListener(new ExitListener());
    }

    public static void setUpFirstPanel() {
        firstPanel.setLayout(new MigLayout("flowy"));
        firstPanel.setBackground(Color.WHITE);

        firstPanel.add(firstPanelLabel, "align 50%");
        firstPanel.add(firstPanelButton, "align 50%");

        firstPanelButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Container contentPane = frame.getContentPane();
                contentPane.removeAll(); // Required for new stuff to appear
                contentPane.add(secondPanel); // Required for new stuff to appear; this _is_ the new stuff, after all
                ((JPanel) contentPane).revalidate(); // Required for new stuff to appear
                contentPane.repaint(); // Without this, old stuff isn't removed before new stuff is painted and is still visible underneath
            }
        });
    }

    public static void setUpSecondPanel() {
        secondPanel.setLayout(new MigLayout("fill, flowy"));
        secondPanel.setBackground(Color.WHITE);

        secondPanel.setPreferredSize(new Dimension(1650, 1080));
        secondPanel.setMinimumSize(new Dimension(300, 200));
        secondPanel.setMaximumSize(new Dimension(1920, 1080));

        textScrollPane1 = new JScrollPane(textArea1);
        textScrollPane2 = new JScrollPane(textArea2);

        textScrollPane1.setBackground(Color.WHITE);
        textScrollPane2.setBackground(Color.WHITE);

        textScrollPane1.setBorder(BorderFactory.createTitledBorder("Text area 1"));
        textScrollPane2.setBorder(BorderFactory.createTitledBorder("Text area 2"));

        textScrollPane1.setPreferredSize(new Dimension(400, 300));
        textScrollPane2.setPreferredSize(new Dimension(400, 300));

        // cell col row [span x [span y]]
        secondPanel.add(textScrollPane1, "cell 0 0 1 1, push, grow");
        secondPanel.add(textScrollPane2, "cell 0 1 1 1, push, grow");
        secondPanel.add(button1, "cell 1 0 1 2, aligny top, growx");
        secondPanel.add(button2, "cell 1 0, growx");
        secondPanel.add(button3, "cell 1 0, growx");
        secondPanel.add(button4, "cell 1 0, growx");
        secondPanel.add(button5, "cell 1 0, growx");
        secondPanel.add(button6, "cell 1 0, growx");
    }
}

class ExitListener extends WindowAdapter {
    public void windowClosing(WindowEvent event) {
        System.exit(0);
    }
}

February 4, 2011

Simple sample to replace the contents of a content pane in Swing


package swingTest;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Main
{
static JFrame frame;

static JPanel firstPanel;
static JPanel secondPanel;

static JLabel firstPanelLabel;
static JButton firstPanelButton;

static JLabel secondPanelLabel;

/**
* @param args
*/
public static void main(String[] args)
{
createObjects();

firstPanel.add(firstPanelLabel);
firstPanel.add(firstPanelButton);

firstPanelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
Container contentPane = frame.getContentPane();
contentPane.remove(firstPanel); // Required for new stuff to appear
contentPane.add(secondPanel); // Required for new stuff to appear; this _is_ the new stuff, after all
((JPanel) contentPane).revalidate(); // Required for new stuff to appear
contentPane.repaint(); // Without this, old stuff isn't removed before new stuff is painted and is still visible underneath
}
});

secondPanel.add(secondPanelLabel);

frame.addWindowListener(new ExitListener());
frame.getContentPane().add(firstPanel);
frame.setVisible(true);
}

public static void createObjects()
{
frame = new JFrame();
frame.setSize(500, 500);

firstPanel = new JPanel();
firstPanel.setLayout(new FlowLayout());
firstPanelLabel = new JLabel("first label");
firstPanelButton = new JButton("first button");

secondPanel = new JPanel();
secondPanel.setLayout(new FlowLayout());
secondPanelLabel = new JLabel("second label");
}
}

class ExitListener extends WindowAdapter
{
public void windowClosing(WindowEvent event)
{
System.exit(0);
}
}