wingS 3.2, March 2008
Copyright © 2005 - 2008 wingS Project Team
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License" .
Table of Contents
This document is intended to serve curious novices to wingS, developers using wingS, and developers willing to look into its internals. The further you progress in this document, the deeper you are getting to the core of wingS.
Chapter 1 provides an introduction to the concepts of wingS. Chapter 2 takes you through a step-by-step tutorial on how to write a wingS application and make it fly. Once you decide to develop an application chapter 3 will lead you through the wingS toolset and mindset. Chapter 4 is an architectural orientation to aid in understanding how wingS works. It tries to fill in enough background information such that a newcomer will be able to quickly understand wingS source code and code examples, since there is plenty of demo code that you can look at.
For management-oriented information please refer to the wingS whitepaper[1].
session.Session - a wingS class name (the org.wings part of the full name is elided, but subnames, as in this example are shown)
"TODO" labels call out to you. Yes, you! Participate in the effort to build a first-class documentation, give something back!
Contributions to this documentation were provided by Holger Engels, Volker Fritzsch, Ian Gardner, Oliver Scheck and Benjamin Schmid.
The Java Foundation Classes (JFC) and its Swing API are known by nearly every Java developer. Its inherent MVC2 model is solid, yet flexible and fairly easy to learn. These reasons led to the idea to create an API with the Swing-feel, but generating and handling web applications. This idea is the underlying theme of wingS.
So where is the Swing in wingS?
Suppose you want to display a table. Having Swing in mind, you'd implement an AbstractTableModel and associate it with a JTable instance which you placed in a JFrame. Now with wingS you do just the same, except you replace J's with S's. That's it! Apart from this prefix character of widget names the code is identical.
For almost every J<xxx> component of Swing you find a S<xxx> counterpart in wingS with similar or identical behavior.
Furthermore, suppose you want to react on mouse clicks (selection events) in your table. Again, doing it the Swing way, you'd provide a ListSelectionModel implementation and register a ListSelectionListener. The exact same code will work in wingS, no difference to Swing.
So wingS relies on the model, listener and event infrastructure of Swing, since there is no need to reinvent the wheel.
With the Swing feel of wingS, you sometimes forget you develop for a web application context. But there are concepts intrinsic to the web that wingS neither hides, nor intends to hide. Understanding these simple concepts helps you in creating successful projects.
Despite all of wingS's magic, at the end of the day a wingS applications is nothing else but a servlet. Quite some servlet though.
This means you will need a servlet container, such as Tomcat, Jetty, or JRun to execute wingS applications.
During application development you typically don't notice the servlet environment your application will run in. In fact, it is only in the web.xml where you need to do anything servlet-related. Still it certainly helps to be aware of it's underlying servlet foundation, as you will see in succeeding chapters.
When developing web applications it proves very useful —if not necessary— to keep business logic separate from graphical representation. wingS provides simple, but empowering concepts for this separation:
From the developers perspective wingS applications are built up using the provided set of Java widgets. Hence a layer is needed to transform the visual properties set on the widget components into the according HTML code. This is done by the so called Pluggable Look & Feels (PLAF). A PLAF is an exchangeable collection of widget renderers called CGs. Every CG is responsible for the rendering of one specific type of widget.
This concept, combined with the fact that wingS ships with a very powerful default PLAF implementation, is a great step into the separation of view and logic, as the typical developer does not need to know any web specific details at all, but can fully rely on using the intuitive set of Swing-like wingS widgets.
Layout managers are responsible for arranging the widgets inside a container widget to a visual composite. These container widgets can be nested inside each other, each using its own layout manager, resulting in a containment hierarchy which will be reflected in the display. There are two classes of layout managers available, a static template layout manager and various dynamic layout managers.
With the static STemplate layout manager you can create a XHTML template, which structurally and visually defines one component or the complete screen layout of your application. Then, put in <OBJECT> elements and give them a name, which will be referred to by your program code. These elements will be replaced with the real components at runtime. See Section 3.7.2, “STemplateLayout” for how this is done.
Besides this static approach you can also utilize different dynamic layout managers to arrange the widgets inside your application by pattern. Their usage is almost identical to that of the layout managers in Swing, and allows you to arrange your widgets in the usual patterns such as gridwise, floating or as a list. The browser-specific PLAF implementation then takes up the job to generate the optimized HTML code to achieve the desired layout. Please refer to Section 3.7.1, “Swing-like Layout Managers” for more details.
The styling of single widgets or the application as a whole heavily relies on Cascading Style Sheets (CSS). It uses CSS to assign specific visual properties, statically to a whole class of a widgets, or dynamically to specific widget instances during runtime by transparently generating new CSS styles on-the-fly.
But don't be afraid! In most cases you will not need to get in contact with these details at all, since wingS already ships with a decent default style which will provide a good start new eye-candy web applications while all the styling needed during runtime (i.e. colouring, specifying other fonts or assign spaces between widgets) can be achieved completely transparent using the Swing-like wingS API.
The chapters Section 3.8, “Styling your application using CSS” and Section 5.4.2, “Interaction between CSS and PLAFs” will give you a deeper view on this.
In real-life applications the process of starting a new wingS application will begin as simply as designing the main application layout. You will probably prefer to use a template to arrange and embed the main application components inside your desired screen design. As an example think of an application with a dynamic menu bar on the left side and a center work area, placed inside your typical corporate identity design.
As the next step you will already be able to start designing and implementing the first functionality of your application. The default PLAF and CSS style sheets provided with the wingS distribution will already give you a decent start for a nice-looking and working web application, fulfilling most of the typical design needs.
After designing the main application layout you have already passed the point where you will no longer need to have any in-depth knowledge of web-related technologies or concepts, in order to implement and design the further application logic. You can solely use and rely on the Swing-like Java API to create, use and arrange the finer grained application components. To dynamically style components, for example to emphasise a label with red color to indicate an error, is just as simple as a label.setForeground(Color.RED); call in your application code.
Only if you need to style the look and feel of your application in detail will you extend or overwrite the default wingS CSS style sheets to adapt it to your individual styling needs. Section 3.8, “Styling your application using CSS” will give you a deeper view into leveraging CSS to style your application.
The following sections will demonstrate all the tasks needed to set up a simple "Hello World" example from scratch using the wingS framework.
Please refer to the doc/tutorial/hellowings folder in the wingS distribution for the sources of this quickstart How-To.
First we need to set up your development environment. Just create a new directory helloworld and helloword/src and copy the contents of the two directories web and lib of the wingS distribution. Start your preferred IDE and create a new project using the directory you just generated. Include all the *.jarfiles in the copied lib and web/WEB-INF/libdirectories in the classpath of your newly created project.
Let's start writing our first wingS application. In general the starting point of a wingS application session is the constructor of a specific application root class. So all we need to do is to write a class which creates a wingS SFrame inside its constructor and adds some components to show before it calls SFrame.setVisible(true) to display the application.
Most IDEs support quick JavaDoc and parameter name lookup if you attach the src/java directoryof the wingS distribution as Java source and the dist/doc/api directory as JavaDoc source at the two main wingS libraries wings.jar.
We don't have any special layout desires, so we won't use a template layout file. For simplicity we'll just use one of the dynamic layout managers to arrange our components. So let's create a HelloWingS.java class in the src directory:
import org.wings.*;
import java.util.*;
import java.awt.event.*;
public class HelloWingS {
public HelloWingS() {
SGridLayout gridLayout = new SGridLayout(1);
SForm panel = new SForm(gridLayout);
SLabel titel = new SLabel("Hello World - this is wingS!");
SButton okButton = new SButton("Guess!");
titel.setFont(new SFont(null, SFont.BOLD, 18));
gridLayout.setVgap(10);
final SLabel message = new SLabel();
final STextField textField = new STextField();
final int randomNr = new Random().nextInt(10) + 1;
// check our guesses and respond with according message
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (Integer.toString(randomNr).equals(textField.getText()))
message.setText("Congratulations! You guessed my number!");
else
message.setText("No - '" + textField.getText()+
"' is not the right number. Try again!");
}
});
// arrange components using a grid layout
panel.add(titel);
panel.add(new SLabel("We want fun, so let's play a game!\n" +
"Try to guess a number between 1 and 10."));
panel.add(textField);
panel.add(okButton);
panel.add(message);
SFrame rootFrame = new SFrame();
rootFrame.getContentPane().add(panel);
rootFrame.setVisible(true);
}
}In the next step we need to set up a web.xml file in the web/WEB-INF directory. This step is only needed once per application and it's only intention is to register the wingS servlet inside the servlet container and point wingS to our newly created application root class HelloWingS. A very minimalist web.xml file would look like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<servlet>
<servlet-name>HelloWingS</servlet-name>
<servlet-class>org.wings.session.WingServlet</servlet-class>
<init-param>
<param-name>wings.mainclass</param-name>
<param-value>HelloWingS</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloWingS</servlet-name>
<url-pattern>/HelloWingS/*</url-pattern>
</servlet-mapping>
</web-app>Please note that if you want to use some advanced, Ajax-based components you have to add a few further declarations in this web.xml file as described in Section 3.5, “AJAX-based componets”
In the last step we only need to compile and package all our application files into a Servlet compatible Web Archive (WAR) file. You could do this either by manually compiling and zipping your application sources, but as this is a reccurring task it's better to let a tool do this task. Apache Ant[2] is a very popular Java based build tool. We'll create a very simple Ant build file which will instruct Apache Ant to package our files as needed into our application WAR file. So just create a new build.xml file in your project directory with the following contents:
<project name="HelloWingS" default="war">
<path id="build.classpath">
<fileset dir="lib" includes="*.jar"/>
<fileset dir="web/WEB-INF/lib" includes="*.jar"/>
</path>
<target name="prepare">
<mkdir dir="build/class"/>
</target>
<target name="compile" depends="prepare">
<javac srcdir="src" destdir="build/class">
<classpath refid="build.classpath"/>
</javac>
</target>
<target name="war" depends="compile">
<war destfile="build/hellowings.war"
webxml="web/WEB-INF/web.xml">
<classes dir="build/class"/>
<fileset dir="web">
<exclude name="**/web.xml" />
</fileset>
</war>
</target>
<target name="clean">
<delete dir="build"/>
</target>
</project>After you've created the Ant build file we can execute it by calling Ant to parse and execute it. Use Ant to execute the war build target in your build file by executing Ant in your project home directory via
<path to your ant installation>\bin\ant war
Apache Ant will automatically look for a build.xml file in the current directory and try to execute the named target. If Ant fails to compile and complains that it does not know where to find the Java compiler you may need to specify the path to your JDK installation in the JAVA_HOME environment variable.
If everything went well you should find a file named hellowings.war in the build directory of your project. So we're almost done. The only thing we need to do is to deploy this WAR file in your servlet container. If you use Jakarta Tomcat[3]the only thing you need to do is to copy your hellowings.war file into the webapps directory, start Tomcat by calling bin/startup.bat and point your browser to http://localhost:8080/hellowings/HelloWingS

[2] You can download the latest version of Apache Ant at http://ant.apache.org/
[3] Jakarta Tomcat is available at http://tomcat.apache.org/
This chapter presents what you need to know to develop with the wingS framework.
Starting a new project with a new technology or framework often implies the need to learn a few things before you are able to fully leverage its benefits. Being confident that wingS has to offer really great advantages to you, when you are about to design and implement new web applications, this chapter tries to give you some hints to help you smoothly start developing applications with wingS.
Table 3.1. Overview of libraries shipped with wingS distribution
| Libary | Required | Description |
|---|---|---|
| wings.jar | yes | The wingS core classes |
| css.jar | yes | Contains the default PLAF implementation for CSS capable browsers |
| commons-logging.jar | yes | Apache Commons Logging API. Will delegate logging to Log4J or the standard java logging facility |
| bsh-core.jar | yes | BeanShell for scripting support in STemplateLayout |
| kdeclassic-lfgr.jar | optional | Icons used in default wingS widget (i.e. graphical checkboxes, icons for table cell editors) |
| dwr.jar | optional | Direct Web Remoting libraries for AJAX support. Refer to Section 4.2, “Client Side Script Listeners” |
| log4j-1.2.9.jar | optional | Deploy and configure Log4J with your application |
| commons-httpclient-x.x.jar | development only | Apache Commons HTTP client used for Section 4.4, “Session Recording and Playback” |
| servlet.jar | development only | Servlet API interface declaration. Only required for compiling as implementation is provided by the used servlet container |
| wingx.jar | optional | Additional highlevel components. |
| cxx.jar | optional | PLAF for the additional highlevel components. |
Here is a short list of tools you'll need or which might be helpful during development of wingS applications:
The latest version of the wingS framework can be always obtained from its website at http://wingsframework.org/. If you want to be in touch with the latest development you can also retrieve a development snapshot by anonymous CVS access.
As a build tool to build and deploy your wingS application as demonstrated i.e. in Section 2.3, “Go! - Deployment in Servlet Container”. Some powerful IDEs like IntelliJ IDEA offer powerful alternatives for quick (re-)deployments during the development phase. But this requires some in-depth knowledge of the required configuration so at first for beginners the common used approach with ant as deployment tool might be the better choice.
Though choosing a browser is somehow a matter of personal taste and preferences, we heavily encourage to try out the Mozilla FireFox browser available via http://www.getfirefox.com/. Beneath being a great browser, there are also some great plugins freely available which can be a great help during developing web applications. Since this is favoured by the core wingS development team, new features tend to work first with FireFox !
Chris Pederick donated these really two great plugins for the FireFox. They will be valuable tools when you are about to style your web application in detail. You can install them via http://chrispederick.com/work/.
Refer to Section 3.8.5, “CSS live editing” to learn how these plugins can help you.
This is a great javascript debugger, html- and css- editor, dom- inspector, ... available at: http://www.getfirebug.com/.
Thinking about the intended architecture of your application before starting with the implementation of the code is always a good idea. Here are some points to think about:
First of all try to determine the basic layout and the logical groups in your web application. Section 3.3.3.1, “Hierarchical nesting of containers” shows an example of an application screen split into various logical areas. For floating layouts the STemplateLayout might be a good choice for the root panel (contentPane).
Introduce your own new components for reccurring composite visual blocks in your application by derivating from the standard components. Examples might be specially styled components or repeating groups of component. You will save time, and your look and feel will be consistent.
Try to keep the application styling at one place. Apart from using only a few, or possibly only one template file for global layout, and creating custom components, its also a good idea to avoid spreading styling instructions all over your code. Instead of manually setting the foreground color to red for error notification labels try either to introduce a new, global CSS style inside your custom CSS file and apply this to the according SLabel, or to create a special derived ErrorLabel subclass which gathers the styling specific code instructions.
For larger projects introduce your own subclasses for all major wingS components, or use a factory pattern. This allows you to modify or extend the default behaviour of specific UI components easily in one place during later phases of the project runtime. E.g. think of enhancing text based SButton with dynamical rendered icons.
Try to continue the MVC concept in wingS inside your application code. This means not to implement the business logic directly inside the UI components, but to delegate i.e. the click of a delete button to a delete(selectedObject) method call in a Controller class collecting all the business logic.
Leverage the event concept by learning how to efficiently use the listeners interfaces provided by the wingS components and introduce your own event based implementations inside your application logic.
Most architecture and design patterns which are a good choice for designing Swing-based applications are also a good choice for designing wingS based web applications. So if you are already familiar with designing Swing applications choosing the right application design for your web application will be an easy task with wingS.
If you took a look at Section 2.1, “Get ready - Writing a wingS Frame” you probably realized that the main thing the entry class does, is simply constructing a new SFrame container component (see also Section 3.3.3, “SContainer”), adding further wingS components to it and finally calling setVisible(true) on this frame. So you probably already guessed that the root of all wingS applications is a frame, constructed in the root class of your application:
public class MyWingSApp {
public MyWingSApp() {
SFrame rootFrame = new SFrame("Window title");
rootFrame.getContentPane().add(...);
// ...
rootFrame.setVisible(true);
}
}Hence you can think of SFrame as an equivalent to a window in common desktop environments like Microsoft Windows or KDE.
Equally, at a more technical level, an SFrame corresponds directly with the dynamically created HTML document that is delivered to the browser as the current view of your web application. During the lifecycle of a typical wingS application session most of the time there will exist exactly one root frame. This root frame contains all the widgets currently visible and can be retrieved at any time via the session manager:
SFrame rootFrame = SessionManager.getSession().getRootFrame();
Though in most cases every session has only exactly one instance of SFrame, there are situations where multiple frames may exist, sequentially or concurrently, during a session. This is especially the case for frameset based applications[4] or wingS applications opening new browser windows for sub dialogs.
Besides regular, single-framed single-windowed web applications wingS offers a great feature: It can simulate Multiple Document Interface (MDI) applications. MDI applications are applications hosting multiple documents or windows inside one parent application.
In wingS this can be achieved easily using SInternalFrame inside a SDesktopPane. The desktop demo shipped with wingS demonstrates in a very impressive way how internal frames can be used to build a multi-document Editor with RSS reader and Drag & Drop support.

Please refer to the source code of the desktop demo for an example of how to use internal frames. It can be found in the demo/desktop directory of the wingS distribution.
Please note that the frameset support in wingS is currently experimental!
Using framesets is a way to separate the content displayed by the browser into two or more different and independent documents. Using framesets you can split the available space of the browser window in different sub-areas each called a Frame. In each Frame the browser displays an independent document. This corresponds exactly with the usage of the HTML frameset tag.
Please don't mix the meaning of the term Frame as used in the context of browser framesets with its meaning in this chapter as instances of SFrame
The following code snippet shows how to construct a new frameset split horizontally into two inner frames:
SFrameSet frameset = new SFrameSet(new SFrameSetLayout("50%,50%", null));
SFrame leftFrame = new SFrame("left frame");
SFrame rightFrame = new SFrame("right frame");
horizontal.add(leftFrame);
horizontal.add(rightFrame);
frameset.setVisible(true);This example created two new inner frames. Each of this frame will probably contain further displayed components. As an action in one frame may influence the presentation of a component in the other frame, this would mean that on every request the whole frameset will get reloaded, i.e. both inner frames will refresh.
The reload manager offers a way to avoid this. To use the reload manager you will need to add an additional, invisible frame to your frameset. In contrast to the default case, only this invisible frame will be used to fire the initial request on any user action. On the server side the reload manager determines which of the inner frames contain modified components, and returns a list of the inner frames to be updated to this invisible inner frame. Hence after receiving the response the hidden reload manager frame will trigger only explicit reloads of those frames whose visual representation has changed.
For a detailed example of how to use framesets and the reload manager please refer to the frameset demo available in the demo/frameset directory of the wingS distribution.
In the previous section you already got in contact with your first wingS component: SFrame and its variants. Now we are about to introduce the root class of all wingS components SComponent. This class is the counterpart of the JComponent class in Swing. As in Swing all wingS widgets are subclasses and hence instances of SComponent.
The following sections we'll describe in more detail about the usage and different between the different SComponent variant. But first we'll explain the commons between all wingS components. Below you'll find a table which points out hand-selected methods with a short explanation of their meaning as declared in SComponent.
Table 3.2. Selected methods and properties of SComponent
| Method | Description |
|---|---|
| get/setName() | The current name or id of this component. It is initially set on demand but can be overwritten with custom values. This name will also appear inside the generated HTML code, hence it must contain only a restricted set of characters. The name must be unique throughout the session! Otherwise event dispatching won't work consistently. |
| getParent() | The parent container owning this component. This is set by the wingS framework at the time point when this component is added to another container like a panel or the root frame of the application. |
| getParentFrame() | The root frame in which this component is placed as being recursively accessible via getParent(). |
| get/setBackground() | Defines an optional background colour for this component. |
| get/setForeground() | Defines an optional foreground colour for this component. On SLabel instances i.e. this will become the colour of the text. |
| get/setBorder() | Apply a border. Gives components a border and/ or applies insets. Besides the well known types SLineBorder, SEtchedBorder, etc. there is the SDefaultBorder, that components have attached by default. It makes the component appear, as defined in the stylesheet (see Section 3.8, “Styling your application using CSS”). If the border property is set to null, there will be no border and no insets and the rules in the stylesheet have no affect. |
| get/setHorizontalAlignment() | Sets the horizontal alignment of this component inside the parent container (LEFT, RIGHTor CENTER). The effect of this value depends on the layout manager assigned the parent container. |
| get/setVerticalAlignment() | As previous method the vertical alignment accepting TOP, CENTER and BOTTOM as valid values. |
| get/setPreferredSize() | Apply a preferred size to a component. You may choose relative percentage values (new SDimension("50%", null)) as well as absolute pixel value as parameters (new SDimension(150, SDimension.AUTO_INT)). Another typical usage is to assign the global SDimension.FULL_WIDTH instance i.e. to a panel or similar component: This achieves that the component is stretched by the browser as far as possible to take up the full available width. Please be aware that the rendering result may vary depending on the browser. This is especially the case for relative vertical dimensions (FULLHEIGHT). |
| get/setStyle() | The current CSS style class. By default the wingS PLAF assigns every component its unqualified Java class name as CSS style name. So for SButton instances this will have "SButton" as default value. Please be aware that the default wingS style definitions are defined using these default CSS class names. If you want to keep the wingS default styles but additionally apply your own styles defined in a separate CSS file, you should append your custom CSS style class names to a component separated by a space (i.e. "SButton myClass"). |
| get/setToolTipText() | Apply a tool tip textwhich will pop up after a short delay when the mouse pointer is over the component. The behaviour of tools tips can be defined more in detail via the SToolTipManager. |
| get/setShowAsFormComponent() | The value of this property influences the technical representation of some components and the submit behaviour of most components. For example, an SList is either rendered as a select tag or as an unordered list (ul) of links. For most components, this decides only, if the whole form will be sumbitted on click or if the form content gets lost and a GET request will be sent. HTML Check boxes are only rendered in browsers if you nest them inside a HTML <FORM> element. Therefore wingS will render them as clickable images if it is not contained in an SForm. You can achieve the same behaviour inside forms on setting this value to false. A very common usage of this property is to force SButton to appear as text links and not browser submit buttons inside forms. |
| setAttribute() | Dynamically set a CSS property value for a component. You can use this method to define any arbitrary CSS property/value pair on the current component. It will be rendered as an inline style. |
| is/setVisible() | By default being true you can use this property to hide any componentfrom the current view. |
| is/setEnabled() | This property has a different meaning on different components. Mainly it is used to defineinput elements as inactive respectively read only. |
Besides the explained methods SComponent declares further methods for:
Adding and removing various event listeners
Applying key bindings and component context menus on a component
Modifying the used component renderer (PLAF)
Requesting the input focus for the next page refresh
You probably guessed it already: SContainer is a derived variant of SComponent that can container further SComponents. Therefore it declares various methods to add other SComponent to it. Furthermore a container offers methods to access and modify the responsible layout manager. But let's start from the beginning.
Contrary to other web applications framework, wingS is tries to be as component oriented as possible. One main concept of component-oriented application design is that you take simple component and arrange them to more complex compound components until you built up your complete application. Hence containers are the key feature for a hierarchical component-oriented design approach. The figure below shows how you can used and nest components to a complete screen dialog.
Similar to Swing the containers are only responsible for holding and managing the list of contained inner components. The layouting of the contained components inside the space taken up by the container component will be delegated to the layout manager. Refer to Section 3.7, “Layout Managers” for more information about details regarding the different layout managers.
After construction you can simply add the components which should be contained inside using one of the add(SComponent component) methods. Please note the variants of the add method allowing you to pass a Layout constraint as additional parameter. Some layout managers, such as the SBorderLayout or the STemplateLayout need additional information about where exactly you want to place the passed component inside their provided layout style, while simpler ones like the SGridLayout will just choose the next available place.
Finally you just need to attach your newly created container to the root container as shown in Section 2.1, “Get ready - Writing a wingS Frame” to make it visible to the user.
The type of container you'll probably encounter most of the times during you every-day writing of wingS application is SPanel. This is the simplest form a container and like every SContainer it will be initialized with a default layout manager (SFlowLayout, can be overridden via default.properties) but you probably will prefer to define your own layout manager.
SPanel panel = new SPanel();
SGridLayout gridLayout = new SGridLayout(2);
panel.setLayout(gridLayout);
panel.add(new SLabel("First name:"));
panel.add(new STextField());
panel.add(new SLabel("Last name:"));
panel.add(new STextField());
panel.add(new SButton("Submit"));
SForm inputForm = new SForm();
inputForm.add(panel);In this example we created a SPanel container, assigned a two-columed grid layout as layout manager and added several components to this container. In the last step we wrapped this container inside another container: SForm.
What's the reason behind the SForm instance in the previous example? If you are aware of HTML and already designed some web pages by coding HTML code directly, you'll probably know that input elements like text fields, submit buttons, radio buttons and so on require to be surrounded by a <FORM> HTML tag.
The SForm container is the object-oriented equivalent for this HTML tag. So if you want to place input elements on your screen you must ensure that the container or one of its parent container is a SForm. Please be aware that contrary to the other containers available in wingS you are not allowed to nest SForm containers into each others! This reflects the same HTML restriction.
Typically it's sufficient if you use one SForm container on the very top of your component hierarchy. The SForm container has also a property which advises the browser to send the input via HTTP POST or HTTP GET calls.
There are some more containers available in wingS. Below you find a short summary of them including a short description of their characteristics.
Table 3.3. Overview of all public SContainer implementations
| Container class | Description |
|---|---|
| SAnchor | A very simple container to wrap a link around the inner components. Typically you use this to wrap SLabel for creating links on external URLs. |
| SDesktopPane | A special container to manager internal frames as demonstrated in the wingS desktop demo. Only needed for MDI applications. |
| SForm | Refer to Section 3.3.3.3, “The SForm container for input elements”. Wrap contained components with a HTML <FORM> tag. May not contain other SForm containers. |
| SMenuBar | A special container accepting SMenuItem object. Used to build a desktop-like menu bar. Refer to the desktop demo. |
| SPanel | The most common and basic container. |
| SRootContainer | Abstract superclass for root containers. Root containers are the topmost containers which are not contained in other containers |
| SFrame | Refer to Section 3.3.1.1, “Frames”. Typically the root container of every wingS session is a SFrame instance. |
| SInternalFrame | Refer to Section 3.3.1.2, “Internal Frames”. A root container for MDI web applications. |
| SFrameSet | Refer to Section 3.3.1.3, “Frameset”. A root container to create frameset based web applications. Will container further SFrame instances for each frameset segment. |
| SScrollPane | A special pane to create paged or graphical scroll panes. |
| STabbedPane | Neat container to create a tabbed stack of various components as demonstrated in the WingSet demo. |
We already showed how to combine and arrange SComponents using containers and simple layout managers. So it is time to know how you can place content in them.
The simplest way to present information to the user is by using labels. You already encountered instances of SLabel in the preceding examples so we will explain in a bit more depth the usage and capabilities of this widget component. Here are some usage examples
// You can just pass the text to display as parameter of the constructor
SLabel simpleLabel = new SLabel("A simple label");
// ...but it can be changed at any time
SLabel emptyLabel = new SLabel();
// ... i.e. when something happened:
emptyLabel.setText("The trigger event occured! \n"+
"Second line of label");
// Extensive formatting can be done done during runtime
SLabel fancyLabel = new SLabel("A green, bold italic label");
fancyLabel.setForeground(Color.green);
fancyLabel.setFont(new SFont(SFont.BOLD + SFont.ITALIC));
fancyLabel.setWordWrap(true); // allow word wrappingAs you can see, the text inside a label can be changed at any time using the setText() bean property setter. Using the properties declared in SComponent the font size, style, face as well as the text and background color can be easily controlled using pure Java code.
Please note that starting with version 2.0 the labels word wrapping behaviour changed to be in sync with Swing. To allow the browser to line-break a label call setWordWrap(true) on it.
As in the Swing JLabel, the SLabel component in wingS also accepts HTML formatting directives like <b>...</b> or <i>...</i>inside the text if you prefix the text with an <HTML> tag. You should use this carefully to avoid mixing of view and logic.
If you assign specific styles on a regular basis to a component like a label, you should consider to declare a static new CSS style class and assign this to the label. This allows you on the one hand to modify this style globally in the style declaration, and avoids on the other hand the generation of many dynamically generated CSS styles generated by the wingS framework. Think for example of error labels you might want to have in a specific style. In your programming code you would additional assign a new CSS class to a label:
SLabel errorLabel = new SLabel();
errorLabel.setStyle(errorLabel.getStyle() + " errorLabel");
errorLabel.setVisible(false); // hide by default
//.. an error occured
errorLabel.setText("An error occured during the last operation");
errorLabel.setVisible(true);This will allow the web designer in your project to declare a new static CSS style an optional application-specific CSS style sheet.
.errorLabel {
color: #f00;
font-weight: bold;
border: 1px solid #ddd;
}Please refer to Section 3.8, “Styling your application using CSS” for more information about using CSS to style your application in detail.
The SIcon interface is your friend if you want to include images in your wingS application. An SIcon can be an image retrieved from an arbitrary source and used in conjunction with various wingS widgets. By default wingS comes with five different implementations of icon providers, each capable of handling a different source as source for the image data to be displayed. Possible sources are
static image files deployed with your application (SFileIcon)
static image files reachable via an absolute or relative URL in your application (SURLIcon)
static image files contained in your classpath (SResourceIcon)
static image data contained in a byte array (SByteArrayIcon)
and dynamical generated Swing ImageIcon images (SImageIcon)
Please note an important difference between the SImageIcon and the other mentioned SIcon implementations: While the content of the others images cannot change during runtime, an ImageIcon can have an updated image content during every request. Allowing you to display any dynamically generated image this has the side-effect that wingS needs to create and send new graphic data to the browser. So to include just image files in your application ensure that you use one of the other implementations as demonstrated in the example below:
// image contained in the class path
SResourceIcon javaCupIcon = new SResourceIcon("org/wings/icons/JavaCup.gif");
// image deployed in a icons directory of you application
SURLIcon okButtonIcon = new SURLIcon("../icons/ok-button.png");
// a file in the file system
SFileIcon background = new SFileIcon(new File("/home/blueshift/background.jpg"));
// Use i.e. just for display
SLabel javaCup = new SLabel(javaCupIcon);
// ..or as clickable button
SButton okButton = new SButton(okButtonIcon);
// or i.e. as container background
panel.setBackgroundImage(background);SIcon images can be used in conjunction with many wingS widgets. But the most commons usage are in combination with SLabel for just displaying a graphic or with SButton to create graphical buttons in your application.
Below you'll just find a list of further pure display-only widgets.
Table 3.4. Further non-input wingS components
| Component | Description |
|---|---|
| SSeparator | Simple implementation of the HTML <HR> horizontal ruler element. Alternatively you can set single border lines on a component like setAttribute(CSSProperty.BORDER_BOTTOM, "1px solid #ccc"); |
| SSpacer | An invisible component that can be used as spacer component. Typically renders as invisible pixel stretched to the desired size. |
| SProgressBar | A progress bar indicator as demonstrated in the WingSet demo. |
Buttons are simple, clickable input components. Instances of a button can present themselves as regular GUI buttons (HTML submit buttons), clickable text links or clickable images. To react to activations of a button, action listeners can be registered on buttons and will receive events notifying them about the click.
To create and display a button just create a new SButton instance and add it to a visible container. By default buttons will appear as regular buttons if they are nested inside a SForm container, otherwise they will appear as simple text links due to constraints of the browser. Please refer to Section 3.3.3.3, “The SForm container for input elements” to learn more about the role of the SForm container. You can force SButtonto render as text links even if contained inside a form using a setShowAsFormComponent(false) method call. Alternatively you can use images as clickable buttons (perhaps dynamically modified at runtime). Below you will find a few example of how to create buttons
// a simple default button
SButton simpleButton = new SButton("ok");
// text link button
SButton textLinkButton = new SButton("Request support");
textLinkButton.setShowAsFormComponent(false);
// a graphical button
SButton graphicalButton = new SButton(
new SResourceIcon("org/wings/icons/JavaCup.gif"));The default style of form buttons are modified in the default CSS files delivered with the wingS default PLAF. You might want to attach a new custom CSS file and modify their appearance there.
Most widgets provide various "listener" interfaces so that you can supply your own callbacks in order to handle these. Here we create a push button and register a callback to receive notification when the button is pushed. Note that the button is declared final so that we can access it within the anonymous callback class
final SButton submit = new SButton("Search");
form.add(submit);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
// ... trigger search
}
};
submit.addActionListener(al);In the above example the callback only receives events when the button is pushed. An alternative is to make the form the listener, since if the form is to have a "default" button (when the user hits the enter key) then it is the form which will be notified. In this case you would add the listener callback to the form instead of the button:
form.addActionListener(al);
The ActionEvent.getSourctree() will determine whether the event comes from the button or the form so you can use the following tests in your callback:
public void actionPerformed(ActionEvent e) {
if(submit == e.getSource())
// submit button pushed
if(form == e.getSource())
// user hit enter key
}You can extract data from the text field as shown above with a call to STextField.getText().
Whenever a listener is attached to a component or its model, wingS assures, that the listener is invoked immediately after the user modifies the component. For example, if you add a DocumentListener to a textfield's document, wingS will add a form submission javascript listener (onchange) to the respective input field.
Input to your wingS application comes in the form of state changes to the various widgets you have created, and in application events that you can listen to. To illustrate this we use the simplest case of a form which contains an entry field and a button.
Recall that user input for entry fields must be contained in a SForm . The first task is to create the form and entry field (layout details omitted):
SForm form = new SForm(new STemplateLayout(...));
// ... add to top pane, add layout
// The following assumes a template layout, the names "W1", "W2"
// identify layout components
SLabel label = new SLabel("Enter stuff:");
STextField text = new STextField("initial value");
form.add(label, "W1");
form.add(text, "W2");The text field variable might be made a class member variable so that you can access it later to retrieve its contents:
String dataValue = text.getText();
Alternatively you might declare it as a local final variable, and access it from within a local closure callback, reacting to user input as shown in the next section.
The example below demonstrates that you can restrict the number of columns independently from the physical size a text input field. Furthermore text areas can be advised to line-wrap the entered text at their physical, virtual size or not to wrap the text themselves at all (default).
// A text field restricted to 40 character and
// a fixed width of 400px
STextField simpleField = new STextField("default text");
simpleField.setMaxColumns(40);
simpleField.setPreferredSize(new SDimension(400, SDimension.AUTO_INT));
// An multilines input field with 6 lines and 40 colmns
// Line will wrap after 40 colums
STextArea textArea = new STextArea(6,40);
textArea.setLineWrap(STextArea.VIRTUAL_WRAP);
// A read only textfield
STextField readonlyField = new STextField("read only");
readonlyField.setEditable(false);You can retrieve the entered text via the getText() method i.e. as an reaction of an "O.K." or "Save" button click as already drafted in the previous examples.
It is worth mentioning a less obvious source of event triggers - the text fields/areas themselves ! You can attach Swing-like document change listeners which will notify every value update of a text component.
textArea.addDocumentListener(new SDocumentListener() {
public void insertUpdate(SDocumentEvent e) {
STextComponent source = (STextComponent) e.getSource();
System.out.println("Text after insertion of " +
e.getLength()+" characters: "+source.getText());
}
public void removeUpdate(SDocumentEvent e) {
STextComponent source = (STextComponent) e.getSource();
System.out.println("Text after removal of " +
e.getLength()+" characters: "+source.getText());
}
public void changedUpdate(SDocumentEvent e) {
// won't happen - refers to style changes, not text changes
}
}); Check boxes and radio boxes are widgets that can be either selected or unselected. While check boxes can be checked and unchecked independently, radio boxes always allow only one instance to be selected within a button group.
Similar to the behaviour of SButton, their appearance differs depending on if they are contained inside a form or not. If those input elements are not contained inside a Section 3.3.3.3, “The SForm container for input elements” element, they will be rendered as graphical widgets. You can force the graphical version, as well as customising the used images for this case.
// You gotta' decide yourself for exact one gender ;-)
SButtonGroup buttonGroup = new SButtonGroup();
SRadioButton optionF = new SRadioButton("Female");
SRadioButton optionM = new SRadioButton("Male");
buttonGroup.add(optionF);
buttonGroup.add(optionM);
// a checked checkbox
SCheckBox agreedBox = new SCheckBox("accept disclaimer");
agreedBox.setSelected(true);
// an unmodifyable checkbox rendered as graphic
SCheckBox disabledCheckBox = new SCheckBox("Umodifyable checkbox");
disabledCheckBox.setEnabled(false);
disabledCheckBox.setShowAsFormComponent(false);
disabledCheckBox.setDisabledIcon(new SResourceIcon(...));Comboboxes are one-lined item selection widgets with further options to choose via a drop-down menu. SLists have two and more lines and support multiple item selection modes[5] additionally to the single selection mode offered also by the combobox.
// A combo box containing a coloured label and two string values
SLabel color = new SLabel(Color.green.toString());
color.setForeground(Color.green);
SComboBox comboBox = new SComboBox();
comboBox.setModel(new DefaultComboBoxModel(new Object[]{ "element1", color, "element3" }));
// A simple selection list
SList multiSelectionList = new SList();
multiSelectionList.setName("multiple");
multiSelectionList.setSelectionMode(SList.MULTIPLE_SELECTION);
multiSelectionList.setListData(new String[] {"Item 1", "Item 2", "Item 3"});You can attach ListSelectionListener to SList instances and ItemListener to SComboBox instances to receive events on selection changes.
comboBox.addScriptListener(ComboBoxCG.JS_ON_CHANGE_SUBMIT);
Last but not least we have to mention two more special input component SFormattedTextField and SPasswordField. The latter one is simply the password entry variant of a STextField meaning that typed characters will appear obfuscated i.e. as asterix instead of being displayed plain-text.
The SFormattedTextField is a conceptual very interesting example of an AJAX based components. To work correctly you must deploy and register the DWR jar and servlet correctly as shown in WingSet example. Below you'll find a usage example
SFormattedTextField numberFormattedField =
new SFormattedTextField(new NumberFormatter());
private static class NumberFormatter extends SAbstractFormatter {
NumberFormat format = NumberFormat.getNumberInstance(
SessionManager.getSession().getLocale());
public Object stringToValue(String text) throws ParseException {
return text == null || text.trim().length() == 0
? null : format.parse(text.trim());
}
public String valueToString(Object value) throws ParseException {
return value == null ? "" : format.format(value);
}
}This component does the following: If you enter the SFormattedTextField you can enter or modify the current value of the text field. If you entered a parseable value, the value of the text field will be updated to the parsed value. Otherwise the input field will regain the input focus and be marked red to indicate an input error.
This is realized in the following way: When you try to leave the text field, e.g. by pressing the Tab key to jump to the next input field, the text field will trigger a XML encoded request to the server in the background. There the DWR servlet takes the request and delegates it to the Java listener (here: NumberFormatter). If it succeeds the value of the text field will be updated with the formatted value returned by this. If it fails and indicates this by throwing a ParseException the cursor will be set back to the input field and it will be marked red to grab the user's attention.
With the STable widgets you can
present data in tabular format
allow the user to make row selections
allow the user to edit the data directly inside the table
According to the paradigms of the MVC pattern this component explicitly separates the contained data from the controlling logic. STable contains two model objects holding the data: The Swing compatible TableModel is the provider of the presented table data while a SListSelectionModel object holds the current selection status.

But let's start step by step
Before you can present a table of data you first have to create a data provider for the table data. The used TableModel data model is actually the one declared by Swing, so the Swing tutorial might give you additional hints about implementing and using a table model.
In the example below we just create a very simple table model showing just a list of names hard coded in a static array. In typical real-life table models you would probably implement table models which operate on a list of objects and implement the getValueAt() in that way that it retrieves the according object from this list and returns an according object property value depending on the column.
// a very simple table model implementation
private static class MyTableModel extends AbstractTableModel {
private final String[] COLUMN_NAMES = new String[]
{"First name", "Last name", "Male"};
private final Object[][] DATA = new Object[][] {
new Object[] {"Michael","Bolton",Boolean.TRUE},
new Object[] {"Samantha","Fox",Boolean.FALSE},
new Object[] {"Eric","Clapton",Boolean.TRUE},
new Object[] {"David","Bowie",null}
};
public int getRowCount() {
return DATA.length;
}
public int getColumnCount() {
return 3;
}
public String getColumnName(int i) {
return COLUMN_NAMES[i];
}
public Object getValueAt(int row, int column) {
return DATA[row][column];
}
}
// .... in the main code
// create new table instance and add to visible container
final STable table = new STable(new MyTableModel());
form.add(table);Below you'll find a screenshot of the resulting visual representation

Having shown a list of data to the object we might want to allow the user to do some operations on the data show. In the first step we'll allow the user to select single rows and offer him operations on them like e.g. deletion. Modifying the table to accept row selections is just as simple as
// allow single-line selection in table table.setSelectionMode(STable.SINGLE_SELECTION);
After this statement you can select rows just by clicking the numbers in the first column. Now we also add a delete button and a status label telling us about the this event
// create a status label and a delete button
final SLabel statusLabel = new SLabel();
final SButton deleteButton = new SButton("Delete selected row");
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
statusLabel.setText("Currently selected row: "+table.getSelectedRow());
// business logic for operation
}
});
// add them
form.add(statusLabel);
form.add(deleteButton);So each time we click on the new presented delete button a status label will be updated telling us the index of the currently selected row. If no row has been selected then we will receive -1 as value. As well as single row selections, tables can also allow selection of continuous intervals (STable.SINGLE_INTERVAL_SELECTION) or discontinuous intervals (STable.MULTIPLE_INTERVAL_SELECTION). According to Swing the SListSelectionModel allows you to retrieve the necessary information about the selected rows via getMin/MaxSelectionIndex() methods for the continuous case, and an isSelectedIndex(int rownum) for the discontinuous case.

Besides explicitly asking the table's selection model about the current selected rows, we can also use a ListSelectionListener to get notified via explicits events when the selection changes. The code below will update the status label every time the selection changes.
table.addSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent listSelectionEvent) {
statusLabel.setText("Selection changed to: "+table.getSelectedRow());
}
});But now we want allow the user to be able to edit the table content. This is quite easy in our simple demo. We have to modify the implementation of the table model so that it tells the table which cells are editable. In our case we just allow all cells to be editable. Furthermore we must overwrite the empty default implementation of the setValueAt() method in AbstractTableModel to accept and store the changed data. Last but not least the getColumnClass() returns the class type of the column values so that the framework knows to choose the right cell editor for the value type. Adding the few lines below to MyTableModel do this job
public boolean isCellEditable(int row, int column) {
return true; // allow for all cells
}
public void setValueAt(Object object, int row, int column) {
DATA[row][column] = object; // just take the canged value
}
public Class getColumnClass(int column) {
return column == 2 ? Boolean.class : String.class;
}And below you see the final outcome of our efforts

Until now the creation of our table was really painless. As we did not override them, the table uses the default cell renderer and editors for rendering the cell contents and providing the appropriate components for editing a cell value. In our simple case this is absolutely fine as we have very simple data types.
But look at the last column. Wouldn't it be better to display this boolean value as checkable check box instead of the toString() representation of the boolean values? This can be easily achieved by overwriting the default cell renderer to return a accordingly checked SCheckBox instead of the default creating cell renderer implementation which just renders a SLabel with the cell content toString() value.
private static class MyCellRenderer extends SDefaultTableCellRenderer {
private final SCheckBox checkBox = new SCheckBox();
public MyCellRenderer() {
// set icon to indicate editable, empty cells
setEditIcon(getSession().getCGManager().getIcon("TableCG.editIcon"));
// render always as graphic icon
checkBox.setShowAsFormComponent(false);
}
public SComponent getTableCellRendererComponent(
STable table, Object value, boolean selected, int row, int col) {
if (value instanceof Boolean && row != -1) {
checkBox.setSelected(((Boolean)value).booleanValue());
return checkBox;
}
else
return super.getTableCellRendererComponent(table, value, selected, row, col);
}
}After implementing our own, extended table cell renderer we just have to assign it to our table and we're done.
table.setDefaultRenderer(new MyCellRenderer());
See the result

Beware that the checkbox is readonly. Clicking it will not update the table model. Making cells editable is the job of a cell editor. Look at the wingSet Table example and XTable example to learn more about editable tables.
Further customization can be applied to tables. Many tables contain a large count of data rows which result in large table size. In this case it is a good idea to make the table scrollable. Please refer to Section 3.4.3, “SScrollPane” for details how this can be done.
As already demonstrated the cell renderers can be exchanged as a whole or for specific column classes. If necessary you can exchange the cell editors in the same way as demonstrated for the cell renderers.
STable also allows to exchange the renderer component for the header cells as well to turn the display of the header cell.
The visibility of the horizontal and vertical separator lines can be easily modified using the setShowHorizontalLines() and setShowVerticalLines() methods of STable.
The spacing and padding between the table cells can be modified with the setIntercellPadding() and setIntercellSpacing() methods.
Extensive adjusting of the table colours is possible. The recommended way is to add a custom static CSS style overwriting the wingS default styles for tables. Look at Section 3.8, “Styling your application using CSS” for details. As alternative there exists also the convenient possibility to all the styling via the Java API and the CSS Pseudo selector objects provided by STable. Just look at the example below
// Really fancy and colourful table
table.setAttribute(STable.SELECTOR_HEADER, CSSProperty.BACKGROUND, Color.blue);
table.setAttribute(STable.SELECTOR_NUMBERING_COLUMN, CSSProperty.BACKGROUND, Color.gray);
table.setAttribute(STable.SELECTOR_ODD_ROWS, CSSProperty.BACKGROUND, Color.orange);
table.setAttribute(STable.SELECTOR_EVEN_ROWS, CSSProperty.BACKGROUND, Color.magenta);
// Special marking as !important necessary due to existing !imporant declaration
// in wingS default styles. Might be obsolete already
table.setAttribute(STable.SELECTOR_SELECTION, CSSProperty.BACKGROUND,
CSSStyleSheet.getAttribute(Color.yellow)+ " !important");... and here is the result

The STree component is a visual explorer-like component with expandable nodes. In many aspects it is very similar to tables with STable, so this section will give only a very quick and short presentation of the STree component. Beginners might first read Section 3.4.1, “STable” to read a more verbose introduction of an advanced wingS components. Furthermore the Swing documentation and tutorials of JTree might also be a good supplementary starting point for newbies.
Similarly to the STable component, the STree component stores its data in a separate model. Therefore it uses the Swing TreeModel for retrieving the data presented in the tree and a STreeSelectionModel object to store the currently selected items.
TreeModel is an interface of Swing declaring methods to retrieve a root object of a logical tree of objects, as well as methods to retrieve the child objects of these node objects. Typical node objects are instances of the TreeNode interface. In most cases you'll just deal with the default implementations of these interfaces provided by Swing: DefaultTreeModel and DefaultMutableTreeNode.
So let's look at a very simple example tree model constructed using these default implementations:
// Create a root node
DefaultMutableTreeNode root = new DefaultMutableTreeNode("STree");
DefaultMutableTreeNode parent;
// Add first child node with more sub nodes
parent = new DefaultMutableTreeNode("colors");
root.add(parent);
parent.add(new DefaultMutableTreeNode("blue"));
parent.add(new DefaultMutableTreeNode("violet"));
parent.add(new DefaultMutableTreeNode("red"));
parent.add(new DefaultMutableTreeNode("yellow"));
// ... second child node ...
parent = new DefaultMutableTreeNode("sports");
root.add(parent);
parent.add(new DefaultMutableTreeNode("basketball"));
parent.add(new DefaultMutableTreeNode("soccer"));
parent.add(new DefaultMutableTreeNode("football"));
parent.add(new DefaultMutableTreeNode("hockey"));
// ... and third and last root child node
parent = new DefaultMutableTreeNode("food");
root.add(parent);
parent.add(new DefaultMutableTreeNode("hot dogs"));
parent.add(new DefaultMutableTreeNode("pizza"));
parent.add(new DefaultMutableTreeNode("ravioli"));
parent.add(new DefaultMutableTreeNode("bananas"));Note that in this example we used simple String object as data model objects for each node. But you can use any custom object type as data model objects but you should ensure that the used STreeCellRenderer is capable of rendering them nicely.
With creation of our tree model much is already done. Just wrap this tree model in a new instance of a STree component, and you receive an expandable visual tree with selectable nodes.
STree tree = new STree(treeModel); form.add(tree);

Equivalent to the table case, the STree component also allows you to register listeners to get notified about selecting items (TreeSelectionListener), nodes that are requested to expand (TreeWillExpandListener) and rows that are expanding (TreeExpansionListener). The expansion notification event could for example be used to lazily load the children from the backend system. The code example belows demonstrates a very simple selection listener which will update a label every time the selection changed
final SLabel treeEvent = new SLabel();
form.add(treeEvent);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
TreePath path = treeSelectionEvent.getPath();
String action = treeSelectionEvent.isAddedPath() ? "Added " : "Removed ";
treeEvent.setText(action + path.getLastPathComponent().toString());
}
});By the way: You can also change the selection and expansion state of the tree programatically by calling the addSelectionPath(), removeSelectionPath(), expandRow() and collapseRow() methods. These methods expect TreePath objects which describe the full node path to the node to select.
A STreeCellRenderer is used to render the values of the tree nodes into the displayed representation. If you decide to choose other data model object this is the place where customization is needed.
Furthermore trees implement the Scrollable interface and therefore can be wrapped into SScrollPane as described in Section 3.4.3, “SScrollPane”.
The indentation width of the tree nodes, the graphics used to indicate the node state (open, closed, etc.) and the selection mode are configurable via the API, too. Please refer to the JavaDoc and the WingSet demo for more details.
The SScrollPane is special container which allows you to display viewports of Scrollable components. Namely Scrollable is implemented by
STable
STree
and SList
A SScrollPane accepts instances of these Scrollable and provides default horizontal and vertical adjustables. By default the scroll pane will use a viewport of 10x10 units. In the case of a table this would mean that you will see maximum 10 rows and 10 columns of the table at once. If the table has more rows or columns the according adjustables will provide the possibility to move the viewport inside the overall range of the table's columns and rows.
In the following example we'll take the demo table as described in Section 3.4.1, “STable”. So - in the simplest case you can make a Scrollable scrollable if you wrap it inside a SScrollPane instead of directly adding it to a container. The modified code from the table example looks like this
// create new table instance and add to visible container final STable table = new STable(new MyTableModel()); // form.add(table); form.add(new SScrollPane(table)); // wrap in a scrollpane
This results in a presentation of the table as shown in the following screenshot

That's all!
Extensive customization can be done on the SScrollPane and its corresponding SAdjustable. Below we present a short summary of the relevant properties and their function
Table 3.5. Overview of customizable properties for scrolling feature
| Property | Description |
|---|---|
| SScrollPane properties | |
| verticalScrollBar / horizontalScrollBar | The instance horizontal / vertical scrollbar. By default semi-graphical SScrollBar but can be also the available SPageScroller or null. |
| horizontalExtent / verticalExtent | The horizontal / vertical extent describes the size of the view port in number of 'units' (table cells, list elements) which can be seen at once. |
| horizontalScrollBarPolicy / verticalScrollBarPolicy | Allows to modify the visibility of the scrollbars. Can be defined to be always visible, only if necessary or never. |
| mode | Sets the scroll mode. See below for supported options. |
| SAdjustable properties | |
| layoutMode | Defines if this adjustable is layouted as a horizontal or vertical adjustable |
| directPageClickables | Property for page scroller |
| unitIncrement | Returns the amount to change the scrollbar's value by, given a unit up/down request |
| blockIncrement | Returns the amount to change the scrollbar's value by, given a block (usually "page") up/down request |
The scrollpane distinguishes three different scroll modes:
Table 3.6. Scroll modes
| Mode | Behaviour |
|---|---|
| COMPLETE | The whole scrollable viewport of the scrollable is rendered. Give it a preferred size! It will be rendered as a scrollable div (overflow: auto), that requires a defined size. |
| SCROLLING | Only the portion defined by the extents will be rendered. The scrollbar's extent spans exactly the scrollable's scrollable viewport. Use it in combination with the standard SScrollBar. |
| PAGING | Only the portion defined by the extents will be rendered. The scrollbar's extent is the first multiple of the respective scrollpane extent, that covers the scrollable viewport. Use it in combination with the SPageScroller. |
Using these properties we know how to modify our scroller in the previous example to achieve a paged view as i.e. known from popular search engines or other websites.
SScrollPane scrollPane = new SScrollPane(table); SPageScroller pageScroller = new SPageScroller(SPageScroller.VERTICAL); pageScroller.setLayoutMode(SPageScroller.HORIZONTAL); scrollPane.setHorizontalScrollBar(pageScroller); scrollPane.setVerticalScrollBar(null); scrollPane.setMode(SScrollPane.PAGING); pageScroller.setExtent(5); form.add(scrollPane);

TODO
Please refer to the WingSet demo an the following section for hints about using desktop like menus.
A component context can be attached to any component. It will appear on right-clicking a component inside a browser. To react on the presented menu items a event listener can be attached to each menu item. See example code below
final SLabel testLabel = new SLabel("This label has a context menu.");
ActionListener menuItemListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String selecteMenu = ((SMenuItem) e.getSource()).getText();
testLabel.setText("This label has a context menu. You selected " + selecteMenu);
}
};
SPopupMenu menu = new SPopupMenu();
SMenuItem cutMenu = new SMenuItem("Cut");
cutMenu.addActionListener(menuItemListener);
menu.add(cutMenu);
SMenuItem copyMenu = new SMenuItem("Copy");
copyMenu.addActionListener(menuItemListener);
menu.add(copyMenu);
SMenuItem pasteMenu = new SMenuItem("Paste");
pasteMenu.addActionListener(menuItemListener);
menu.add(pasteMenu);
SMenu subMenu = new SMenu("Help");
SMenuItem about = new SMenuItem("About");
about.addActionListener(menuItemListener);
subMenu.add(about);
SMenuItem topics = new SMenuItem("Topics");
topics.addActionListener(menuItemListener);
subMenu.add(topics);
menu.add(subMenu);
testLabel.setComponentPopupMenu(menu);
form.add(testLabel);
Besides the AJAX based incremental update feature contained in wingS, some components use also AJAX mechanism to provide a better and smoother user expierience. Those components use the DWR library as communication mechanism. Currently these are namely:
SFormattedTextField
XCalendar
XColorPicker
XInplaceEditor
XSuggest
Please ensure that your web.xml contain the following declaration of the DWR servlet, so that they work properly:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
<description>Do we startup in debug/test mode?</description>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>DEBUG</param-value>
<description>How verbose do we want logging to be?</description>
</init-param>
<init-param>
<param-name>org.directwebremoting.extend.CreatorManager</param-name>
<param-value>org.wings.plaf.css.dwr.SessionCreatorManager</param-value>
<description>Install a session local CreatorManager</description>
</init-param>
<init-param>
<param-name>org.directwebremoting.extend.AccessControl</param-name>
<param-value>org.wings.plaf.css.dwr.SessionAccessControl</param-value>
<description>Install a session local AccessControl</description>
</init-param>
<init-param>
<param-name>org.directwebremoting.extend.Remoter</param-name>
<param-value>org.wings.plaf.css.dwr.SessionRemoter</param-value>
<description>Install a session local Remoter</description>
</init-param>
<load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>this is because DWR calls are handled by a separate servlet, the DwrServlet, which needs to be configured accordingly (with another <servlet> and <servlet-mapping> section). You can integrate and write your own components if required and use the DWR communcation mechanism.
The previous sections already demonstrated in depth some sample events fired by the presented wingS components and how you can use custom implemented event listeners to react to these events. The table below points out a few more listener interfaces provided by wingS
Table 3.7. Selection of listener interfaces provided by wingS
| Listener | Provider | Description |
|---|---|---|
| SRenderListener | SComponent | This listener will receive events before starting and after finishing the rendering of a component |
| SRequestListener | Session | Receives notifications about the different phases during the HTTP request processing of wingS. Refer to SRequestEvent for a documented list of these different phases. |
| SParentFrameListener | SComponent | Receives events on adding and removing a component from a parent container. Use this if you need to modify or initialize things depending on the owning container, as the container is not set during construction time of a component. |
| SContainerListener | SContainer | Receive events on every component added or removed from a container |
| SComponentListener | SComponent | To be notified about changed visibility of a component via setVisible() method calls |
| SDocumentListener | STextComponent | Listener provided by text areas and input fields notifying about changes in the underlying 'document' which can be retrieved via the getText() method. |
| SExitListener | Session | Listener to get notified on programatically destroying of the wingS user session. If this session exit is initiated by calling the Session.setExitAddress() method you can throw a veto exception to abort the session destroy (i.e. to prompt for unsaved values). |
| SInternalFrameListener | SInternalFrame | Listener for receiving events on opening, closing, iconinfying, deiconifying, minimizing and maximizing of internal frames (windows) |
| SComponentDropListener | DragAndDropManager | Listener to get notified about DragSource objects dropped to a DragTarget |
| SInvalidLowLevelEventListener | SFrame | Events listener that gets notified about outdated request (mainly due to browser back navigation or double clicks). Used for back button detection and handling logic. |
As mentioned already in section Section 1.2.2, “Separating View and Logic” the layout managers are the components responsible for arranging and layouting your wingS widgets components on the screen. Therefore the container components like SPanel declare a setLayoutManager() method accepting instances of SLayoutManager. Container components can be nested into each other, so even very complex application layouts can be easily assembled using the simpler layout managers.
wingS ships with two main classes of layout managers: the static STemplateLayout and a set of dynamic layout managers similar to those present in Swing.
The static STemplateLayout uses a HTML template file as layout source, inside which you declare fixed and named positions for your components to arrange. So this layout is the best choice if you have exact notions how the resulting HTML for this container should look like. Hence this layout manager is typically used to implement the main screen layout of your web application arranging the main components like the menu bar, working area or notification elements in an template reflecting your corporate website design.
The set of powerful dynamic layout managers arrange the contained components by various patterns. Their advantage is their capability to handle an arbitrary number of components without the need to know and declare the resulting layout positions already during development time.
If using relative preferred sizes, please understand, how layout calculations are performed in the browser. If you give an element 100% width or height, this is 100% of the available space. A typical desktop layout, that spans the whole browser window requires, that all containers, starting from the frame's content pane, have a preferred size of SDimension.FULLAREA.
The border layout is quite similar to the Swing version of the border layout. Contrary to the other dynamic layout managers, it offers only a limited number of five layout positions which can be used to place exactly one component in each. These positions are named North, South, East, West and Center and must be specified as layout constraints on adding components to a border layout.

The border layout tries to take up the full space of the surrounding container component if the owning container has no preferred size defined. As in Swing the Center position is preferred in taking up remaining space, while East and West try to be as narrow, North and South as flat as possible.
The implementation of the border layout tries to achieve the desired layout using an invisible HTML table. Optionally you can define horizontal or vertical spacings between the different sections. Find a commented example usage in the code snippet below
// Example usage of a border layout
SBorderLayout borderLayout = new SBorderLayout();
SPanel borderLayoutPanel = new SPanel(borderLayout);
borderLayoutPanel.add(new SLabel("Center"), SBorderLayout.CENTER);
borderLayoutPanel.add(new SLabel("Top"), SBorderLayout.NORTH);
// add a right-aligned component in the EAST section
SLabel rightLabel = new SLabel("Right");
rightLabel.setHorizontalAlignment(SConstants.RIGHT_ALIGN);
borderLayoutPanel.add(rightLabel, SBorderLayout.EAST);
// Demonstration of additional capabilities of SBorderLayout:
// o Define horizontal/vertical spacings
borderLayout.setHgap(10); // pixels
// o Draw borders around sections. Invisible by default
borderLayout.setBorder(1); // pixelsThe box layout it a very simple layout allowing to take an arbitrary number of components. It simply arranges the contained components either in a row or in a column. Similar to the border layout this is achieved using an invisible HTML table, so a horizontal/vertical spacing can be optionally defined as well as the option to draw a border around the different boxes.

The box layout does not wrap in either horizontal or vertical direction. If you want your components to wrap to the next line if the space is not sufficient you might take a look at Section 3.7.1.4, “SFlowLayout and SFlowDownLayout”. Here is a short code snippet to demonstrate the usage of the box layout in wingS
// Align components in a line
SBoxLayout horizontalLayout = new SBoxLayout(SBoxLayout.HORIZONTAL);
horizontalLayout.setHgap(10); // spacing of 10px between components
horizontalLayout.setBorder(1); // show borders around boxes
SPanel panel1 = new SPanel(horizontalLayout);
panel1.add(new SLabel("Horizontal box layout with padding & border"));
panel.add(new SLabel("Next column"));
// Very simple default usage
SPanel panel2 = new SPanel(new SBoxLayout(SBoxLayout.VERTICAL));
panel2.add(new SLabel("Component 1"));
panel2.add(new SLabel("Component 2"));The grid layout is some sort of a combined vertical and horizontal box layout. It takes a column count as argument in the constructor and will line break when the maximum column count has been reached.

Similar to the preceding layout it is implemented using an invisible layout table. Border and spacing can be defined in the same manner as demonstrated before.
// A 3-columned grid layout
SPanel gridPanel = new SPanel(new SBoxLayout(SBoxLayout.VERTICAL));
gridPanel.add(new SLabel("Component 1"));
gridPanel.add(new SLabel("Component 2"));
gridPanel.add(new SLabel("Component 3"));
gridPanel.add(new SLabel("Component 4"));
// ...A special property renderFirstLineAsHeader allows you to render the first line of the invisible HTML table with <TH> elements versus the default <TD> elements. This might be useful if you want to apply special CSS styles to the first row in these layouts.
The flow layout come in the two flavours of SFlowLayout and SFlowDownLayout. Due to browser limitations SFlowDownLayout is in wingS actually semantically the same as a vertical box layout (we can't flow vertically in web browsers). But the SFlowLayout might be an interesting alternative to your for a horizontal box layout if you don't know the horizontal space available to a container but nevertheless want to utilise it as best as possible without imposing horizontal scrollbars if the components don't fit in a row.
Actually the flow layout advises the browser to flow every of its contained components. This means that the browser should start a new row if a component doesn't fit into the previous row anymore. Just look at the WingSet demo section Dynamic Layouts. Choose SFlowLayout in the drop down box and look how the layouted components behave on resizing your browser window.
The usage is as simple as it could be
// A horizontal flowing layout
SPanel flowPanel = new SPanel(new SFlowLayout());
flowPanel.add(new SLabel("Component 1"));
flowPanel.add(new SLabel("Component 2"));
flowPanel.add(new SLabel("Component 3"));
// ...The flow layout follows the alignment of the previous layout for horizontal and vertical spacing. Hence you can also apply a horizontal alignment to the layout. Why this? Actually the flow layout only takes up the width it requires. So the horizontal alignment of components affects the alignment inside this taken up width. In contrast the alignment defined in the flow layout instance itself affects the horizontal alignment of this composite in the parent container. Take a look at WingSet if you didn't get it already.
Please be aware that this layout might act somewhat surprisingly regarding the preferred sized of contained or surrounded components, and when in combination with table based layouts.
Suppose you put a component with a preferred size of 100% inside a flow layout you'll notice that your components won't flow anymore. This is just because in this case the sized component will adapt the available width rather than the flow layout being able to know that it should use the available space. A similar effect can happen on put a flow layout managed container together with other components inside a parent container. Some components will take up a minimum width though it may visibility not be apparent.
So just in case you encounter problems with non-flowing flow layouts try to play with the preferred sizes of the surrounding or contained components as well as the nesting of your containers. Advanced users may use the techniques described in Section 3.8.5, “CSS live editing” together with a peek into the generated HTML sources to debug the responsible components or size disabling the flowing more effectively.
The gridbag layout is the most powerful dynamic layout manager available in wingS. Similar to SGridLayout the SGridBagLayout allows to align components in a tabular manner but offers many more options to influence the structure of the generated (invisible) layout table. Namely these options are
Horizontal and/or vertical joining of selected layout grid cells to larger layout grid cells
Adding components with the constraint to take up the remaining rows or columns in the layout grid
Specifying weights for the heights and widths of layout cells for modifying the relative space taken by the layout grid cells
Adding components randomly at specified layout grid coordinates
Actually this layout managers can be used in so many different ways that we won't describe them in detail but present some commented usage examples. Please refer to the WingSet demo or the documentation of the Swing variant for an in-depth explanation of it usage.

The code below is the code for the first grid in the screen shot above
// Vertical adding using pre-defined gridx
SGridBagLayout layout = new SGridBagLayout();
layout.setBorder(1); // to show grid structure
SPanel gridBagPanel = new SPanel(layout);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridheight = GridBagConstraints.REMAINDER;
gridBagPanel.add(new SLabel("1"), c);
c.gridheight = 1;
c.gridx = 1;
gridBagPanel.add(new SLabel("2"), c);
c.gridheight = GridBagConstraints.REMAINDER;
gridBagPanel.add(new SLabel("3"), c);
c.gridheight = 1;
c.gridx = 2;
gridBagPanel.add(new SLabel("4"), c);
gridBagPanel.add(new SLabel("5"), c);
c.gridheight = GridBagConstraints.REMAINDER;
gridBagPanel.add(new SLabel("6"), c);
c.gridheight = 1;
c.gridx = 3;
gridBagPanel.add(new SLabel("7"), c);
gridBagPanel.add(new SLabel("8"), c);
gridBagPanel.add(new SLabel("9"), c);
c.gridheight = GridBagConstraints.REMAINDER;
gridBagPanel.add(new SLabel("10"), c);The SCardLayout is again a very simple layout manager nearly identical with the according Swing variant java.awt.CardLayout. It can be understood as a deck of 'cards' with exactly one 'card' visible at once. So you just add your various components to components to a card layout and from the start it will just show the first added one. Using special show(), next() and previous() methods you can 'flip' to another card during runtime.
So actually naming this manager a layout manager is somehow exaggerated as the only thing it does is to ensure that only component in the 'card deck' is visible at one time. Anyhow this layout might be useful if you have a set of different dialogue components you alternately want to present to the user without the need to add and remove them from an empty container. Again you'll find for illustrational purpose a few example code lines below
// A card deck layout
SCardLayout cardLayout = new SCardLayout();
SPanel stackedPanel = new SPanel(cardLayout);
SLabel label1 = new SLabel("Component 1");
stackedPanel.add(label1);
stackedPanel.add(new SLabel("Component 2"));
stackedPanel.add(new SLabel("Component 3"), "layoutconstraint3");
// select visible 'card' by passing specific object instance
cardLayout.show(label1);
// ... or select shown card using the layout constraint
cardLayout.show("layoutconstraint3");
// or flip to next, previous, first or last card
cardLayout.next(stackedPanel);
cardLayout.previous(stackedPanel);
cardLayout.first(stackedPanel);
cardLayout.last(stackedPanel);
Contrary to the other layout managers the STemplateLayout is a static layout manager. Like any other layout manager it allows to place arbitrary elements, but you can write a simple HTML-page being the template for your container component. Though we encourage the use of the dynamic layout managers, this layout manager can be very useful in realising the main page layout of your web application.
To use this layout manager you have to define a template file required by the STemplateLayout instance. Inside this template file you can insert inside your custom HTML code desired wingS objects using tags like <input name="compname"> or <object name="compname"></object>
The name attribute of these input or object tag is the name you have to use as layout constraint when you add the desired component to the template layout managed SContainer: panel.add(new SLabel("a test label), "compname"));
Sample template file:
<HTML><BODY>
Name der Person: <COMPONENT NAME="NAME"><BR>
Vorname der Person: <COMPONENT NAME="VORNAME" ICON="vorname.gif"><BR>
Wohnort der Person: <COMPONENT NAME="WOHNORT"><BR>
</BODY></HTML> According Java sample code:
templateContainer.setLayout(new STemplateLayout("templatefile"));
templateContainer.addComponent(new SLabel("Haaf"), "NAME");
templateContainer.addComponent(new SButton("Armin"), "VORNAME");
templateContainer.addComponent(new SLabel("Neu-Ulm"), "WOHNORT"); Besides this simple inlining mechanism the STemplateLayout manager has also another very powerful feature - specific components bean attributes can be overwritten by specific optional inline attributes attached to your object HTML tags e.g. <object name="compname" background="#ff0000" text="new text"></object>
This feature can be very useful if a web developer is solely responsible for the whole application Look & Feel. Depending on the type of the arranged component they can modify different visual properties (background color, display text, etc.) just by modifying the template file. For example let us assume you are placing the label object from the previous example in your template file. The web designer can overwrite the component's background, display text, etc. by modifying the object inclusion tag to <object name="aConstraintName" background="#ff0000" text="new text"></object>
Please refer to the JavaDoc of the PropertyManager interface and its implementors to find out which properties are supported for which classes.
Just for the sake of completeness we need to mention SRootLayout here. Actually it is a derived variant of STemplateLayout which actually expects only one component named content. By default it uses a very simple default template file template/default.thtml which is provided in the default libraries with wingS. Use this layout for a quickstart of the layout of the root frame of your application if you intend to replace it later on with a more complex STemplateLayout based layout.
Section 3.7.2.1, “Setting component properties via the template” showed you already how you can set specific component properties via special attributes on the <OBJECT> tag in you template files. Additionally you can define Java code fragments in your template file. Just add an attribute SCRIPT on the OBJECT tag with the desired, arbitrary Java code parseable by bean shell[6]. For security reasons bean scripting support is disabled by default and you have to enable it explicitly by setting a property in your web.xml
<init-param>
<param-name>wings.template.beanscript</param-name>
<param-value>TRUE</param-value>
<description>Enables bean scripting in templates files</description>
</init-param>After you enabled it, you can use the SCRIPT attribute in your template files on the OBJECT tags. The DefaultPropertyManager initializes the bean scripting environment with two variables: component is the inline wingS SComponent and session holds the current wingS Session object. Just look at the example template file fragment below
<object name="theLabel" foreground="#990000"
script='component.setText("Value set via template: "+new java.util.Date());'></object>Besides being curious about how to build the presentation logic of your application's layout you are probably also very interested in how you can influence the look & feel of it. Generally the overall style of an wingS application as a whole, as well as that of a single component, is the outcome of the following aspects:
The HTML code generated by the renderers of the used Pluggable Look & Feel. (PLAF).
Static CSS styles applied to this generated HTML code (wingS default style sheets extended with your optional application specific style sheets)
The dynamic styles defined and applied during runtime by modifying visual component properties via Java
In this section we'll explain how these three aspects play together and the approaches to realize your custom design requirements.
But before digging into the depths of CSS you should assure that you have basic knowledge about the following topics.
CSS in general
Available set of CSS properties and valid values
Inheritance and cascading of CSS definitions
Addressing elements inside a HTML document using CSS selectors
The web offers many valuable resources on this topics so it should be easy to learn the required knowledge or better ask a web developer you trust for support on creating the required CSS definitions to style your application. Beware that the interpretation of CSS, especially the support for CSS selectors is very different among browsers!
In short Cascading Style Sheets (CSS) allow you to set visual property values on addressed elements of a HTML document. The cascading effect here is, that if there exists different, contradictorily property values for the same document element, then the CSS definition which addresses this element in the most specific way 'wins'. If this is still ambiguous, then the value which was assigned as last one will be the 'winner'. This is important, because it explains how you can override the default styles in wingS with your own static style sheet as well as why CSS properties defined during runtime in wingS are overwriting the static declared ones.
Okay - the next thing you need to know about are the different possibilities to address a specific element in a web page using CSS selectors and how these possibilities are utilized inside wingS. In CSS you can assign property values to document elements by addressing them via CSS selectors referring to
All general HTML language element types
CSS classes assigned to specific elements using the class="xyz" attribute
elements reachable only via a specific path
elements with specific id assigned via the id attribute
If you take a look in the HTML code wingS generates and sends to the browser you will notice the following.
Every wingS component is rendered as an independent block of HTML code. Containers render themselves using the layout manager, which goes through the list of contained components and generates the required HTML code to reflect the desired layout
Thus every component has its root element. Typically this element wears the id attribute with the component's name and a class attribute with the component's style. The style defaults to the component's simple class name.
The id and the style class make the component addressable by means of CSS rules. You can alter the style property using the setStyle() method of SComponent. Please be aware that most of the wingS default styles operate on the default CSS class, so this will probably have the effect that this component loses most of its wingS default styling. If you want to keep the default style and just assign additional CSS class, the just append your CSS class name to the existing ones separated by a space character.
So below you'll just find a short example HTML fragment of the WingSet demo illustrating the described patterns
<table class="SLabel" id="o2">
<tbody><tr><td>
<span style="white-space: nowrap;">Hello wingS</span>
</td></tr></tbody>
</table>
In wingS the effective style sheet cascade is as follows:
First the wingS default style sheets for the according browser is declared in the wingS root frame. They contain declarations based on generic HTML attributes (i.e. IMG) as well as on the default CSS class names (i.e. SButton).
As next the static style sheets you optionally attached to the root frame are declared. As this CSS file is declared as last one, your customized value will win by default against equal-prioritized declarations from the default style sheet.
Last, the dynamic styles of the components (setAttribute() but also setForground(), setBorder(), etc.) are rendered as inline styles in the page. They will override the definitions of all attached stylesheets.
The default wingS distribution contains different style sheets mainly for two browsers: One specialized for the Microsoft Internet explorer and one for all standard conform browsers (FireFox, Mozilla, Konqueror). The special style sheet for the Microsoft stuff is only necessary as IE 6 still lacks on standard functionalities like CSS child selectors but on the other side requires really a quite huge workaround for all the curious effects it 'features'.
You can find those default CSS style sheets in the wingS css.jar library as org/wings/plaf/css/browsername.css. These default style sheets declare the CSS declarations needed for correct operation of wingS as well as the values for the wingS default application style.
During session initialization the FrameCG determines the browser of the client and attaches the respective style sheets.
By default, a border.DefaultBorder is applied to all components. This special border renders no inline attributes. Thus the styles as defined in the stylesheet(s) take effect. If another border is set, that border's style attributes are rendered as an inline style attribute and override the stylesheet definition. If the border is set to null, an inline style with no border and no padding is written.
Below you'll find an example of how to register a custom CSS style sheet file in your wingS application. In the case shown, the new styles are held in a global, static style sheet file. Hence the style sheet is added to the main frame to ensure it can apply to all components.
frame = new SFrame();
frame.addHeader(new Link("stylesheet",
null, "text/css", null,
new ClasspathResource("../css/myapp.css", "text/css")));The style sheet contains various customised styles, in this case a custom table header:
.STable TH {
background-color: #FFCC99
}Note that all components have a SComponent.setStyle() method to override their CSS style class (i.e. the name for the CSS class which bundles together their display attributes). The individual display attributes are stored in a AttributeSet member. See for example SComponent.setAttribute().
This section mentions briefly a very effective way to incrementally create custom style sheet files for your applications. In Section 3.1.2, “Useful tools” we already mentioned the value of the FireFox browser and the freely available Web Developer plugin.
Actually this plugin is a really great help during styling your application. After installing it, a new toolbar will appear in your browsers. So just visit your current wingS applications and navigate to the dialog you want to restyle. Activate "Show CSS style information" and your cursor becomes a crosshair. If you click now on a specific component, the CSS properties currently applying to this component will be shown in a new sidebar windows. So this is the ideal way to get a quick impression how the current style is assembled in the CSS cascade. Note also the path written in the status bar and the bottom of the browser. This can help you to know how a good CSS selector could look like.
Now let's get ready to rumble! Choose "Edit CSS" in the toolbar of the Web Develeloper plugin. In your side bar at least two tabs with CSS file content will appear. One of those CSS files is the wingS default CSS file for the FireFox browser, the other might be empty as it contains the dynamically generated CSS styles. Browse through the wingS default CSS style sheet file. Try to modify existing CSS declarations. You should instantly see the changes on the screen. If you already attached a custom CSS style sheet to you application, try to do the changes inside that file. Then you can just copy & paste those changes into your original sources or even choose to save the modified file directly out of the Web Developer plugin.
Sometimes it may be hard for you to figure out how to achieve your desired style. The a look into the generated HTML page sources might help. Again FireFox is a valuable tool here for you. Just mark the according section in your browser and right-click the mouse. After choosing "View source for selection" FireFox extracts and displays you only the source of your selected area. This can help vastly to avoid long searches for the right places in very complex web layouts.

wingS allows you to enable client side debugging. In order to use this feature, you need to include the following parameter in your web.xml:
<init-param> <param-name>wings.client.debug</param-name> <param-value>true</param-value> </init-param>
Setting this parameter enables a couple of features, which are toggled via setting a cookie on the client with the name "DEBUG" and a colon separated list of settings. The exact features are described below.
By default, wingS compresses the included Javascript files in order to save bandwidth. For this task wingS uses the packer originating in the DOJO project.
The generated Javascript code is not human readable. If you want to debug an issue in the Javascript code, add the setting "javascript" to the cookie. The included Javascript files are then included in a human readable version. As a bonus, the debugging console firebug lite is included as well. Just hit [F12] to make it visible.
if you are using the included firebug lite console or are using an own debugging console, you can switch the enabled log levels. For example, just add "loglevel=info" to the debug cookie to disable all loglevels beneath "info", namely "debug". Only log messages with a log level of "info" or higher will be printed to the debugging console.
The wingS session is the root instance of a user application session. Every time you start a new browser or a new client contacts the wingS servlet by calling the entry URL of your application, a new HTTP session will created and an according wingS session object will be bound to it. Refer to Section 5.1, “Session Management” for full details about the interaction between a wingS session and the servlet container session. The current wingS session can be always retrieved using the session manager. Furthermore all SComponent offer a convenience method either
Session session = SessionManager.getSession(); // or Session sameSession = aComponent.getSession();
Again - this session is the root of an user application session. It means it is holding the root objects for all session specific data. Mainly it hold references to the root UI component in the UI object hierarchy tree you construct during runtime by constructing new components and adding them to containers. This root UI object can be retrieved using the getRootFrame() method offered by the session object.
One very important usage of the session object is to hold and store application specific data inside a global session context. Think i.e. that your application might want to now the current logged in user at various time during application runtime. So you would use the session to store the user after successful login and use getters to retrieve it from the session again. There is a utility class SessionLocal for typesafe access to session properties.
static final SessionLocal<String> userid = new SessionLocal<String>();
// ...
user.set(SessionManager.getSession().getServletRequest().getUserPrincipal().getName());
// ...
String userid = user.get();
// ...
The session objects also holds various informations like the current used character encoding for the communication with the browser (Default: UTF-8), the current session Locale, informations about the browser, an object describing some session statistics like request count and average response time and the current servlet context, HTTP request and HTTP response objects.
Additionally you can use the session to declare a new URL to redirect the request to or an exit URL do request a vetoable session exit with subsequent redirect to a salutation page.
Please refer to the API docs for details about these features.
The problem of mime parsing and multipart decomposition is completely hidden from the wingS developer. Including a file upload facility in a form is just a matter of adding an SFileChooser component to the form.
SForm form = new SForm();
final SFileChooser chooser = new SFileChooser();
SButton button = new SButton("upload");
form.add(chooser);
form.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
File file = chooser.getFile();
String fileType = chooser.getFileType();
// ...
}
}); Of course there can be multiple file choosers per form. You can limit the size of files to be uploaded in order to avoid denial-of-service (harddisk, bandwidth) attacks You can modify the maximum content length to be posted with the maxContentLength property of the Session. This is 64 kByte by default, so you might want to change this in your application. Please refer to the JavaDoc of the component to learn more about upload related error handling.
One way of providing a download is implementing your own Servlet with the Servlet API directly:
public class XLSServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HSSFWorkbook sheet = HSSFWorkbook();
response.setContentType("application/vnd.ms-excel");
ServletOutputStream out = response.getOutputStream();
out.write(sheet.toBytes()));
response.flushBuffer();
}
} Another way is to extend a DynamicResource and overwrite its write() method.
public final class DynamicPDFDownload extends DynamicResource {
public void write(Device out) throws IOException {
DeviceOutputStream stream = new DeviceOutputStream(out);
writePDF(baseDir, stream);
stream.flush();
out.close();
}
} Then you can externalize it via the ExternalizeManager.
Map/*<String,String>*/ headers = new HashMap/*<String,String>*/();
headers.put("Content-Disposition", "attachment; filename=" + getFilename() );
String url = SessionManager.getSession().getExternalizeManager().externalize(pdfDownload, headers); The default config files reside inside the jar file. Take a look at the jar container to learn what you can do with them. They should be documented. If you want to overwrite any of the settings in the config files, you can do that by providing a file in the WEB-INF directory of your web application. The naming scheme is the following: If you want to overwrite some properties from the file at classpath org/wings/plaf/css/default.properties, you need to put a file named org.wings.plaf.css.default.properties into the WEB-INF directory. That file will then be read on top of the default file, so you just need to overwrite the settings you want to change.
The files that I know of you can change via this method are: org/wings/plaf/css/default.properties and org/wings/util/charset.properties.
By default, if an exception occurs inside a wingS application, this error is logged via the logging mechanism. No other action is triggered. However, you can provide the user with an error page. For this, you need to provide a parameter in your application's web.xml file, like this:
<init-param> <param-name>wings.error.template</param-name> <param-value>/templates/ErrorTemplate.thtml</param-value> </init-param>
The value of the parameter is a path to a wingS template relative to the directory the web application resides in. In this template you can provide the following constraints:
EXCEPTION_MESSAGE - replaced with the message of the exception
EXCEPTION_STACK_TRACE - replaced with the stack trace of the exception
WINGS_VERSION - replaced with the wingS version and compile time
For an in-use example of this, look at the web.xml and ErrorTemplate.thtml files of the WingSet demo.
Cherish the following principles and wingS projects will be a smooth and enjoyable ride.
STemplateLayout - Understand how this layout manager works. The minimal overhead of using it pays off quickly. Your web designer will be able to style your application without touching your Java code. Read Section 3.7.2, “STemplateLayout” for details.
SRootLayout - Set a SFrame 's layout to a custom SRootLayout referencing a template file. The source code looks like this.
URL url = Thread.currentThread().getContextClassLoader().getResource("layout.thtml");
frame.setLayout(new SRootLayout(url));Ensure that the template contains the element object with attribute content . This element will be replaced with the frame's content.
<div id="title">Application Title</div> <hr /> <object name="frame"></object> <object name="windows"></object> <hr /> <div id="footer">company, copyright, license notice</div>
This is a convenient way to define XHTML like a border or title appearing on every page, no matter what the content will be like.
The total sum of testing cycles quickly justifies the investment of learning how to efficiently deploy a web application into the servlet container.
If Jakarta Tomcat is your choice, learn how to use the so-called "manager". With its predefined ant tasks you can install and remove web applications in the wink of an eye without the expensive stop and start of the complete Tomcat and without writing it into the webapps folder first.
Alternatively, think about just deploying the context.xml to webapps folder and pointing its base to your project's build folder which then, of course, has to obey the webapps folder rules.
Beginning with Tomcat version 5.0 a new default session manager was introduced, called SimpleTcpReplicationManager (a subclass of StandardManager ). It attempts to load the persistence storage on application startup and produces a warning-level exception looking like this:
SEVERE: Exception loading sessions from persistent storage java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: org.apache.catalina.loader.WebappClassLoader
To use the the non-clustered session manager use the following section in your web application's context definition in /META-INF/context.xml.
<context>
<manager
className="org.apache.catalina.session.StandardManager"
pathname="">
</manager>
</context>Beware of the consequences of using a servlet container. Multiple requests can traverse static methods in parallel!
Avoid setting visual features in Java code, instead define CSS classes with SComponent.setStyle("xyz")and reference a CSS where you determine the rendering with .xyz { <your style definitions> }.
TODO: Explain loading as resource and benefit of changing the file at runtime.
[4] browser window internally divided into different documents
[5] Typically triggered using the Shift-key and Strg-key in the browser when clicking on items.
[6] Refer to http://www.beanshell.org/
The back button was conceived in the very beginning of the web when static content prevailed. Today's web landscape is dramatically different. The majority of content is created dynamically and web applications bring state and transactions into play.
As intuitive and helpful back button navigation is using static pages, web application developers often wish they could just disable[7] it. Yet, we developers have to live with users clicking on back whenever they please. And let's face it, putting a message "Do NOT click the back button!" is a miserable solution, manifesting the incapacity of the applications creator.
Therefore wingS gives you the power to detect and control the behaviour of back button events. The approach relies on our epoch checking mechanism. It enables you to handle the browsers back button navigation in three different ways:
Default behaviour - ignore requests from old views and redisplay just the current view
Allow back navigation - do nothing on back navigation, allow manipulations and clicks on hand-selected components
Back navigation detection - advise the browser to reload the page on every back navigation and try to detect this event
Let's take a look at the details in the next three sections.
This mode is the default and safest way to cope with the browser back facility. It ensures that the user can only operate on the current view and is not able to perform any operations on expired views, which might lead to unexpected results.
Let's look at a short example how frameworks like Apache Struts or Sun JSF might react. Suppose you were building an email application presenting a list of received emails with a delete button beneath each list entry. After clicking such a delete button, the application would present a confirmation dialog. If you had built your application in a way that the delete buttons referred to an index, a browser back click and click on another delete button would delete the wrong email! In the default configuration wingS avoids this by ignoring the last deletion click and it will just redisplay the first confirmation dialog.
Please keep in mind, that you will be most likely using the HTTP POST operation to receive any form data from the browser. On pressing the back button this will typically display a query window, asking the user if he really wants to submit his last data again.
Another approach supported by wingS is to allow back navigation and support operations on hand-selected components. In this case three things must be triggered.
Firstly, SForm needs to use HTTP GET to avoid the nagging confirmation dialog of the browsers on back navigations.
form.setPostMethod(false);
Secondly, disable page expiration. Otherwise every back navigation leads to a page reload presenting the current view again.
form.getParentFrame().setNoCaching(true);
Lastly, you have to disable the epoch checking on the desired components.
anAlwaysValidComponent.setEpochCheckEnabled(false);
On disabling the epoch check on a component you should be aware that consequently this component might receive input events at any time from developer's perspective. You must ensure that the action triggered will always lead to valid actions in your application. If you obey this you will be able to build web applications with the full comfort of modern web browsers and the comfort of object-oriented design.
The last technique implemented in wingS is to determine back navigations heuristically. Therefore the root SFrame offers a method to declare a virtual back button. But first we'll take a quick look behind the scenes.
As mentioned wingS uses the epoch check feature to detect back operations. For this operation mode we do the following:
Advise the browser to reload every page immediately (default).
rootFrame.setNoCaching(false);
Register a virtual back button.
rootFrame.setBackButton(virtualBackButton);
To ensure that this feature will work reliably you should use a global SForm element contained in every page. This form component will have it's own epoch like all other input elements. As we advise the browser to refresh every page on each entry, a browser's back navigation will lead to an immediate reload of the last page. This last page will also contain the (expired) form component and therefore lead to an InvalidLowLevelEvent. Hence, the root frame will be notified about this InvalidLowLevelEvent and interpret this as a result of a back navigation event. After triggering the virtual back button all further InvalidLowLevelEvent will be ignored for a short period of time to avoid multiple triggers and/or false detected back navigations as a result of inadvertent double-clicks on the client side.
wingS applications typically work with server side events. That is, whenever you press a button in a wingS application, a form is submitted or a link is pressed that calls the server side's registered ActionListener. From a user's point of view this creates the brief annoying pause between request and response, an effect intrinsic to web applications in general. Interestingly though, users have silently accepted this shift in UI behaviour from client to web applications.
Is that acceptance by the user an excuse for not trying to improve this? No. In fact, there are several fascinating initiatives[8] determined to improve the latency of web applications.
Back to wingS. This is how you can integrate JavaScript in a wingS application:
TODO: Explain general concept in wingS.
explain JavaScriptEvents, JavaScriptListener and how they can be added to components
explain a simple application with a JavaScript Listener doing just a submit().
explain JavaScript listeners, that reference other components like in this example from wingset/JavaScriptListenerExample
STextField firstField = new STextField();
STextField secondField = new STextField();
STextField sumField = new STextField();
/*
* Add the client side script listener. The variables
* in curly braces are replaced by the actual IDs of the components.
*/
private final static String JS_ADD_SCRIPT =
"document.getElementById('{2}').
getElementsByTagName('input')[0].value" +
" = ((1.0 * document.getElementById('{0}').
getElementsByTagName('input')[0].value)" +
" + (1.0 * document.getElementById('{1}').
getElementsByTagName('input')[0].value));";
SComponent[] jsParams = new SComponent[]{firstField, secondField,
sumField};
JavaScriptListener jsListener;
jsListener = new JavaScriptListener(JavaScriptEvent.ON_CHANGE,
JS_ADD_SCRIPT,
jsParams);
firstField.addScriptListener(jsListener);
secondField.addScriptListener(jsListener);
sumField.addScriptListener(jsListener);wingS supports Drag & Drop in a very simple but powerful manner. A component that implements the DragSource interface is transparently made draggable by means of a javascript library on the client. A component that implements DropTarget will accept those draggables. When a drag and drop gesture has been performed on the client, a request is sent to the server and component drop listeners are notified.
private class SDragLabel extends SLabel implements DragSource {
private boolean dragEnabled;
public boolean isDragEnabled() {
return dragEnabled;
}
public void setDragEnabled(boolean dragEnabled) {
this.dragEnabled = dragEnabled;
if (dragEnabled) {
SessionManager.getSession().getDragAndDropManager()
.registerDragSource((DragSource)this);
} else {
SessionManager.getSession().getDragAndDropManager()
.deregisterDragSource((DragSource)this);
}
}
}
private class SDropLabel extends SLabel implements DropTarget {
private ArrayList componentDropListeners = new ArrayList();
public void addComponentDropListener(SComponentDropListener listener) {
componentDropListeners.add(listener);
SessionManager.getSession().getDragAndDropManager()
.registerDropTarget(this);
}
public List getComponentDropListeners() {
return componentDropListeners;
}
}The wingSet demo contains a comlete example exploring the drag and drop feature.
Session recording allows you to record a sequence of events by simply clicking through your web application. The recorded data is dumped into a Java source file which - after compilation - can be used to replay the recorded events at a selected pace.
The code should be generated with the components' names (if available) instead of the generated IDs. This makes it easier to identify events in the recording:
<init-param> <param-name>wings.event.usenames</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>wings.servlet.lookupname</param-name> <param-value>Recording</param-value> </init-param>
If a component has a name (name property of SComponent), the component receives events for this name as well (so-called named events). Therefore you must set an application-wide unique name for every component you want to click or modify.
SButton button = new SButton("click");
button.setName("UniqueName1");For session recording, you can use any available test tool (for example JMeter). Alternatively, there is a servlet filter (org.wings.recorder.Recorder), that can be used to generate java sources for a client, that performs the recorded requests:
<filter>
<filter-name>Recorder</filter-name>
<filter-class>org.wings.recorder.Recorder</filter-class>
<init-param>
<param-name>wings.servlet.recorder.file</param-name>
<param-value>Recording</param-value>
</init-param>
<init-param>
<param-name>wings.servlet.lookupname</param-name>
<param-value>Recording</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Recorder</filter-name>
<servlet-name>WingSet</servlet-name>
</filter-mapping>The filter appends a control panel at the end of the generated page. The text field sets the name of the recording. It will be used as the classname and filename of the generated source. The "start" -button starts the recording. The "stop"-button stops the recording and the sources will be generated. The java source file will be placed in the current directory of the servlet engine / application server JVM. Copy it to the wings main directory.
The playback tool (replay.sh) compiles the source and runs it in single-client mode (regression test / functional test) or with n clients in parallel (load test):
replay.sh -d 1.0 http://localhost:8080/wingset/WingSet Recording 20
The playback tool supports slow/fast motion, load ramp, i iterations, n parallel clients.
This chapter describes some more rarely techniques you'll propably only need in very specific cases.
Architectural requirements often suggest a break-up of a web application into multiple servlets. Sharing common resources can be achieved with the ServletContext interface's methods for handling attributes.
To make a resource (any Java object) available across servlets use the setAttribute() method:
ServletContext context = SessionManager.getSession().getServletContext();
context.setAttribute("app.resource", new SharedResource());Within a second servlet use getAttribute() to retrieve a reference to the resource:
ServletContext context = SessionManager.getSession().getServletContext();
SharedResource resource = context.getAttribute("app.resource");Attention has to be paid to the attribute's namespace, as it might interfere with other servlets. Prefixing the package name is an advisable convention.
The approach presented above particularly makes sense if you have decided to use a mixed approach of wingS and other Servlet API based components as ServletContext is a Servlet API interface.
In contrast to earlier version of wingS, the character encoding used to communicate between the browser and the wingS framework no longer defaults to local dependent character encodings, but UTF-8. This enables wingS to display and process input containing all characters represented in UTF-8.
Currently all wingS supported browsers are capable of handling UTF-8 correctly, so this should be no issue. Please be aware that you might need additional fonts installed on your system (i.e. Arial Unicode in Microsoft Windows environments) to be able to display e.g. chinese characters.
Nevertheless you have the possibility to override this default with your own character encodings depending on the locale configured in the client browser. Just provide a properties file called org.wings.util.charset.properties inside your WEB-INF directory with a map of Locale names to character encoding names. Refer to the example file in the WingSet demo or look at the charset.properties in the wingS distribution for details.
Please be aware of one open pitfall in wingS: Currently the template files for the layout managers are all expected to be in the ISO-8859-1 encoding. So you need to escape special characters. This issue might have been resolved meanwhile.
The Spring Framework is used as a way to wire components together, using Inversion of Control and the Dependency Injection pattern, while providing many useful features for enterprise application development.
It could also be used in wingS to hook service objects, data access objects, security, localization and transactional management and so on, transparantly, into the UI framework using a simple XML configuration file. You can also use it to actually assemble the entire user interface part of your application, if you carefully design the UI component object model (i.e assemble panels and forms and so forth into frames).
As there is no direct support (yet) for bootstrapping and inject components into a wingS application, one has to resort to standard servlet session techniques as described here.
The first thing you have to do is of course to have a Spring context, and initialize that in the same webapp context as your wingS application.
Create a default Spring context in web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>That context is by default configured by a file named applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> ... </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> ... </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> ... <bean name="categoryDao" class="se.curalia.pics.CategoryDao"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean name="pictureDao" class="se.curalia.pics.PictureDao"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
Since neither our WingServlet, nor our main wingS class is configured and created by Spring, we need support code in our main class to get to these DAOs. True IoC is therefore never achieved.
Lets use this snippet of code in our main class:
public class MainApplication {
public WebApplicationContext spring;
public MainApplication() {
// get the Spring context from one of Springs util classes
Session session = SessionManager.getSession();
ServletContext ctx = session.getServletContext();
spring = WebApplicationContextUtils.getWebApplicationContext(ctx);
// give the SFrame a reference to our main class
MainFrame mainFrame = new MainFrame(this);
mainFrame.setVisible(true);
}
}Where WebApplicationContext is a Spring class repesenting our newly created context. The main class is then passed down the hierachy via reference so that our UI components can use the following code to fetch Spring configured beans like this:
PictureDao pictureDao = (PictureDao)app.spring.getBean("pictureDao");This approach is not very satisfying beacuase of the need to pass around a reference to this main class (or the "spring" property). Another idea is that the main class fetches the relevant Spring beans and it itself "injects" those beans into the SFrame and the other components. This way the components are luckily unaware where the services and daos etc, came from (again, the idea of IoC and DI). However, this may lead to another problem; the main class must know about every bean, component and service it has to wire together, something that our Spring configuration is best at. One could also argue that it is also a violation of the Separation of Concern paradigm and leads to problems with beans of different scopes (request, session, application etc)
On the other hand, if we perhaps could let every component themselves decide where, and when, they wanted our Spring beans, we could provide them with a little utility class like this one:
public class Spring {
private static Object getBean(String name) {
Session session = SessionManager.getSession();
ServletContext ctx = session.getServletContext();
WebApplicationContext spring = WebApplicationContextUtils.getWebApplicationContext(ctx);
return spring.getBean(name);
}
public static CategoryDao getCategoryDao() {
return (CategoryDao)getBean("categoryDao");
}
public static PictureDao getPictureDao() {
return (PictureDao)getBean("pictureDao");
}
}That way, every component can use a simple call like in this example to fetch a Spring bean:
public class PictureTableModel extends AbstractTableModel {
PictureDao pictureDao;
public PictureTableModel() {
// get our Spring bean
this.pictureDao = Spring.getPictureDao();
}
public int getRowCount() {
this.pictureDao.count();
}
...
}Again, this leads to potential problems. First, the UI component (or the Spring class) must know the name of the bean as configured by Spring and it can't easily be changed. Second, it is not very IoC/DI. your Spring configuration and application development need to be much more integrated than otherwise whould have been needed.
However, regardless of these problems the benefits of using any of these methods can be, and is, leveraged the more you get into Spring.
We dont cover the possibilites when combining wingS WingServlet with Springs servlet support here, suffice it to say that then your wingS application can be bootstrapped and configured by Spring and each application can have its own Spring context in addition to this shared context.
Of course, it doesn't solve all of our problems if not every component is created by Spring. Thats where AOP can could help...
Developers interested in the architecture of wingS will find this chapter useful as it helps understanding the source code.
A "session" is a logical conversation between one particular web browser and the server. Recall that HTTP involves request-response conversations between the web browser and the server. A session can span multiple such request-response conversations, and in the wingS case will typically be initiated when the wingS application is started. It will be terminated when the application explicitly closes the session, the web browser is closed or the session times out. Due to the nature of HTTP the server cannot tell automatically that a web browser is no longer "interested" so it must fall back on using a timeout. For better security it is recommended to provide your wingS application with an explicit "logoff" command, and encourage your users to use it ! (You can do this with a call to session.Session.exit)
The session is managed by the servlet container, usually by associating a cookie with the name "JSESSIONID" with the HTTP requests. The particular value of the cookie then identifies to which session the request belongs. This mapping is all part of the standard Java servlet framework. This in turn delegates handling of each request to session.WingServlet - you will set up the mapping between your web application name and this class as part of your servlet container configuration. (See the servlet tag in web.xml in an example application)
The main servlet class, session.WingServlet, handles requests directly for resources that do not change (e.g. static images), otherwise it creates a session.SessionServlet instance appropriately for each new session and delegates to it. The session's session.SessionServlet is thus the main coordinator and router of events to your wingS application code. Each instance will be associated with an instance of your main application class (which you configured using the "init-param" named "wings.mainclass" in your servlet container configuration). It also creates a session.Session instance which holds some useful instance data as well as sharing the implementation burden with session.SessionServlet. This instance is also transiently associated with the thread which is processing the HTTP request (since this will not change for the duration of the request) using session.SessionManager so that you can conveniently retrieve the session from your own application code using: SessionManager.getSession()
The first task for session.SessionServlet is to extract any parameters from the HTTP request, since this is how visual components will notify of some action like a user pressing a button, and to then trigger the appropriate wingS events. This is the stage where your application code will receive callbacks for any action listeners that you have registered, and in turn drive your whole application's logic. This is handled by passing any HTTP request parameters to the session's session.LowLevelEventDispatcher instance.
After delivering all events session.SessionServlet needs to deliver the externalised representation of the appropriate resource as the HTTP response. If the requested resource indicates that it is for the default resource, i.e. the top level frame of your application, then it looks for the topmost frame associated with the session and "displays" this. Otherwise the request will be for an explicit resource, probably this will have been generated automatically using the externalisation framework when "displaying" some sub-component of the user-interface. It is rare that you need to explicitly know how to address user interface components. (This is covered in more detail later in this document).
There is a magic identifier "_" which can be used to force the display of the top-level frame. This is very useful for debugging, and also for navigation back to the "home page" of your application.
Top level frames are derived from SFrame. The show/hide methods actually add/remove the frame from the collection maintained by session.SessionServlet. This is how you can switch the "main window" in your application. Hence you can keep multiple windows "in the background" by holding the instances in your main application class, and switch rapidly between them.
Your main application instance will be garbage collected when the session finishes, and typically you will access any "top level" application objects via this instance. I.e. where in a normal application you might have global static variables, these would be member variables of your main application instance in a wingS application. You can of course still use global static variables, if that is what you intend, in which case they will have a lifetime that encompasses all sessions.
If you need to integrate some other piece of web technology you can use the functionality of the standard Java servlet HttpServletRequest and HttpSession classes to communicate any common data required by different modules by using named session attributes. This keeps the code decoupled. For example:
public void setSessionFoo(HttpServletRequest request, MyFoo foo)
throws ServletException {
HttpSession session = request.getSession();
session.setAttribute("MySharedFoo", foo);
}
public MyFoo getSessionHelper(HttpServletRequest request)
throws ServletException {
HttpSession session = request.getSession();
Object x = session.getAttribute("MySharedFoo");
if (x == null)
throw new ServletException( ... );
return((MyFoo) x);
}Resources provide a bridge between the user interface components and the externalisation mechanism. This allows user interface components to focus on high level tasks such as responding to events and ignore the messy issue of how to render themselves. A resource must be able to do two key things, which are explicit in the base class declaration of Resource:
Be referred to by a URL, hence implements URLResource
Render itself onto the HTTP response stream, hence implements Renderable
URLs can be entirely arbitrary, in which case use a SimpleURL (e.g. to refer to another web application), or they can be relative to your web application context in which case use a RequestURL (e.g. for a resource deployed under your web application deployment directory). For further options see AbstractExternalizeManager flags. Resources will choose the type which is most suitable for themselves.
The following resource classes are provided by wingS:
StaticResource - base class for components whose representation does not change, e.g. images. Much of the implementation can be put into this base class so it has methods to write the raw data and represent its URL.
ClasspathResource - a static resource that is loaded by a class loader. This is useful when you want to jar up a bunch of resources (e.g. images) along with your java classes for deployment to the servlet container. You can then refer to them using your class namespace, e.g. "com/acme/myapp/prettypicture.jpeg". You may not need to use this directly (wingS uses it for static stylesheets), but you are very likely to need the derived ResourceImageIcon class which is for static images, and would in fact be required for a JPEG file.
FileResource - a static resource wrapped around a file stream. If the resource is an image then use the derived FileImageIcon, which will try and deduce dimensions.
StringResource - use this if you already have the raw representation data of some static resource as a string.
DynamicResource - base class for components whose representation changes, i.e. most user interface widgets. Because of the dynamic nature the URL of these resources is not constant, it is forced to a unique value on each subsequent request by an "epoch counter". This allows wingS to check that a request is for the most current representation of a resource, which is also an important security feature.
DynamicCodeResource - actually only used for SFrame components. Delegates externalisation to the frame.
As mentioned above resources provide the bridge to externalisation and this means that all resources must be able to refer to each other, so all resources must be assigned an id that can eventually be used in a URL or HTML form, (see Resource.getId). However this is not as simple as generating a new random string for each new object since the different resources listed above need different strategies for how to be referenced. Hence this process is managed by different externalizer.ExternalizeManager instances. This class (and its base class externalizer.AbstractExternalizeManager) maintains the mapping between id and resource. This happens on-demand when Resource.getId is called, which in turn calls externalizer.AbstractExternalizeManager.externalize. Note that at this stage no HTML is written, it simply initialises the resource so that it can be addressed. Once this is done a externalizer.ExternalizedResource instance is used to hold various bits of information that will be needed for subsequent externalisation, such as MIME type and whether any special HTTP headers are needed.
The final stage of externalisation (called from SessionServlet) will call on the appropriate externalizer.ExternalizeManager to write an externalised representation of the externalizer.ExternalizedResource. This is delegated to an externalizer.Externalizer implementation specific to the MIME type of the resource (since this is how HTTP identifies different resource data types).
There is also a system-wide manager, externalizer.SystemExternalizeManager, which is used for resources which are not bound to a session (e.g. static files). The session.WingsServlet identifies these requests at an early stage since the resource id starts with a minus character ("-").
The wingS framework needs to understand user actions in the web browser, and then map these to user interface events in the code executing in the servlet container. Most of these events are derived from standard Java SDK classes such as java.awt.AWTEvent. Recalling the limitations of HTTP, these events must be encoded in the HTTP request, either in the URL or in posted form data. Hence the first task for session.SessionServlet is to extract any parameters from the HTTP request, and pass these to the session's session.LowLevelEventDispatcher instance. This extracts encoded events then passes them on to any LowLevelEventListener instances which have registered interest - these will be the actual user interface widgets like buttons, lists and tables.
The dispatcher does some initial decoding of the HTTP request parameters, for example, an additional value may be encoded into the HTTP request parameter name using a "_" separator. It then passes the events on to the listeners using LowLevelEventListener.processLowLevelEvent() . The specific widget will then turn this "low level" event into something semantically meaningful like a button selection. Widget implementors have to go to some trouble to ensure that events fire in the correct order so events are categorised as
Low level - the raw events from HTTP request
Intermediate level - representing some kind of widget state change "in progress"
High level - what you as a wingS API user are typically interested in
Conceptually this should be fairly clear, you will see lots of logic related to this if you dig around the code, but this is unlikely to concern you unless you are implementing your own widgets. The main challenge is that an HTTP request will deliver multiple changes all in one go, unlike a classical application where all events would be nicely serialised. Hence the dispatcher has to try and work out a sensible set of events to deliver, and to delay some state changes so that components do not end up in an inconsistent state due to the order in which events fire.
Externalisation is the process of rendering the wingS user interface in the HTTP response to the web browser. As explained in the section on resources, the first stage is to make resources referenceable by assigning an id. The final stage, which this section describes, is to write the actual representation on to a stream.
First the session.SessionServlet must create an output stream. It uses a factory instance, io.DeviceFactory to create the appropriate io.Device derived stream. You can configure your own device factory class using the "wings.device.factory" init-param of your wingS application servlet. This is a very useful hook for debugging the raw output.
The session.SessionServlet locates the top-level frame, and then calls externalizer.ExternalizeManager.deliver on its externalizer.ExternalizedResource. This method sets up various HTTP headers appropriately for the kind of resource (e.g. MIME type, caching) and then it delegates to externalizer.Externalizer.write(). Different derived classes are used depending on resource type. For example images must be appropriately encoded in GIF or PNG format, whereas static and dynamic resources delegate to the interface Renderable. This is probably the simplest interface, but can have a very complicated implementation! The majority of objects you are likely to be interested in externalise themselves via the Renderable interface.
The rendering task is delegated to separate classes called CGs (Code Generators). Different CGs for a single component can handle browser specifics in a clean way. If you require special rendering for a certain component or if have developed your own custom components, you will have to supply renderers for them. For example you could implement an accordian style CG for an STree with a model of depth 2. A complete set of CGs for all components can be bundled in a PLAF (Pluggable Look And Feel). Every CG should derive from AbstractComponentCG. The latter will render borders, apply the preferred size, provide a tool tip and a component pop up menu, make it draggable / a drop target, etc. The concrete CG needs to care only about the characteristics of the respective component.
The wingS project is hosted by SourceForge at http://sourceforge.net/projects/j-wings. Here you find the latest releases, the CVS repository, and our mailing lists.
Project members on the mailing lists wingS-users and wingS-developers provide fast and thorough answers to your questions.
Professional support for wingS can be obtained by
Version 1.2, November 2002
Copyright © 2000,2001,2002 Free Software Foundation, Inc.
Free Software Foundation, Inc., 51 Franklin
St, Fifth FloorBoston,
MA,
02110-1301,USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
Version 1.2, November 2002
The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
GNU FDL Modification Conditions
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Sample Invariant Sections list
Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
Sample Invariant Sections list
with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.