Pure Java web development.
Dance is an open source, very simple, yet powerful, component based architecture for developing web applications in pure Java.
As HTML, CSS and JavaScript development becomes increasingly complex, the idea is to boost productivity by creating a consistent API, manage complexity, and at the same time, bring all of Java’s features to front-end programming.
This is in comparison to classic HTML/CSS/JavaScript development.
- Obviously, no cumbersome and time consuming HTML and CSS handcrafting.
- No messy mix of markup and Java code.
- Layout managers handle component layout.
- Seamless integration between front-end and back-end.
- Less room for human errors when HTML, CSS and JavaScript are automatically generated.
- Component based development.
- Easily extensible - if needed, making custom components is very straightforward.
- Readable and easy to understand front-end code.
- Less complex and much smaller code base.
- Much easier maintenance.
- Consistent code documentation.
- Most often, no need for testing in different browsers.
- Far better options when developing reusable code.
- Possibility to use encapsulation to manage complexity.
- More rapid prototyping and mockups.
- Common layouts that should be straightforward and intuitive to set up, now is.
- Much better error handling.
- Much less hard to find bugs and peculiarities.
- Easy for any Java developer to pick up front-end development.
- The web development team can focus on a single programming language.
- One language means better communication between front-end and back-end programmers.
- All features in modern advanced Java IDEs can be used in front-end programming.
- Inherently simple architecture that anyone with just some Java skills will understand.
- No need to load huge CSS or JavaScript libraries or plugins.
- Generated code is automatically minimized.
We appreciate all kinds of comments and suggestions on how to improve the project. If you like to contribute code, you’re more then welcome. Please contact the maintainer.
This software is distributed under the BSD 3-Clause License.
A Java EE servlet 3.0 container server, like Tomcat, Jetty, GlassFish, JBoss, WebLogic, etc.
Just download the JavaDance04.jar file and add it to your class path.
We need two classes, a controller and an action. The controller maps a request to the action. For this example, the controller and it's actions need to be in the same package.
HttpControl.java
import javax.servlet.annotation.WebServlet; import org.javadance.basic.http.HttpController; import org.javadance.basic.resources.Configurator; @WebServlet(urlPatterns = {"/danceguide/*"},loadOnStartup = 1) public class HttpControl extends HttpController{ public static HttpControl instance; @Override public void init(){ super.init(); instance=this; } }
HelloWorldAction.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class HelloWorldAction extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ return new Text("Hello, world!"); } }
Fire up your favorite servlet container and enter this URL in the web browser
http://myhost/danceguide/helloWorldAction
where myhost may be something like localhost:8080 if you run locally, or your domain name if you run on a remote server.
To make a link, we create a new action.
LinkAction.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class LinkAction extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ LinkPanel linkPanel = new LinkPanel(); linkPanel.setContent(new Text("Click to show Hello world")); linkPanel.onClick(new HelloWorldAction()); return linkPanel; } }
http://myhost/danceguide/linkAction
LinkPanel is used to create a link to HelloWorldAction from previous example. Clickning the link will execute the HelloWorldAction.
To add components together, we use layout manages. They compose content and manage layout properties. Content can be any component, a text, link, or other layout managers. By combining layout manages, complex layouts can easily be achieved.
LayoutAction.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class LayoutAction extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ HorizontalLayout hLayout = new HorizontalLayout(); hLayout.setGap(10); hLayout.add(new Text("Left")); hLayout.add(new Text("Right")); VerticalLayout vLayout = new VerticalLayout(); vLayout.setGap(10); vLayout.setPadding(10); vLayout.setBorder(new Border()); vLayout.add(new Text("Top")).setHAlign(Align.CENTER); vLayout.add(hLayout); vLayout.add(new Text("Bottom")).setHAlign(Align.CENTER); return vLayout; } }
http://myhost/danceguide/layoutAction
This example shows how to send parameters with an action to the server. Clicking the button will execute the action itself and print the parameter value.
ParameterAction.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class ParameterAction extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ //Get the parameter value String paramValue = context.getRequest().getParameter("paramName"); //Create an action and add a parameter ParameterAction action = new ParameterAction(); action.addParameter("paramName", "You clicked the action button"); //Create a button to execute the action Button actionButton = new Button(); actionButton.setText("Action »"); actionButton.onClick(action); //Layout the component VerticalLayout layout = new VerticalLayout(); layout.setPadding(10); layout.setGap(5); layout.add(actionButton); //If there is a parameter value, add it to the layout if(paramValue!=null){ layout.add(new Text(paramValue)); } //We always need the window panel WindowPanel windowPanel = new WindowPanel(); windowPanel.setContent(layout); return windowPanel; } }
http://myhost/danceguide/parameterAction
This example is similar to the previous one. It shows how to create and submit a form.
FormAction.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class FormAction extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ //Get the parameter String paramValue = context.getRequest().getParameter("paramName"); //Create a text field InputField textField = new InputField(); textField.setName("paramName"); textField.setValue("You submitted the form"); //Create a submit button Button submitButton = new Button(); submitButton.setText("Submit"); submitButton.onClick(new SubmitForm(new FormAction())); //Layout the components VerticalLayout layout = new VerticalLayout(); layout.setPadding(10); layout.setGap(5); layout.add(textField); layout.add(submitButton).setHAlign(Align.RIGHT); //If there is a parameter value, add it to the layout if(paramValue!=null){ layout.add(new Text(paramValue)); } //This is a form, don't forget the form panel FormPanel formPanel = new FormPanel(); formPanel.setContent(layout); //Window panel is always needed to render a document WindowPanel windowPanel = new WindowPanel(); windowPanel.setContent(formPanel); return windowPanel; } }
http://myhost/danceguide/formAction
We don’t really want any front-end code in the actions. That’s where the business logic should be. Instead, we create a page component and an action that views the page. This is a responsive three columns page. We keep using the same Controller.
ResponsivePage.java
import java.io.Writer; import org.javadance.basic.gui.*; public class ResponsivePage extends GuiComponent{ @Override public void render(Writer out) throws Exception{ final Media MOBILE = new Media(0,600); Text menu = new Text("This might be a menu"); Text content = new Text("This is some content"); Text footer = new Text("Here goes the footer"); Image image = new Image("http://danceguide.sparkney.com/256px-Two_dancers.jpg"); image.setRelativeWidth(100); image.setTooltip("Dance image"); BasicLayout layout = new BasicLayout(BasicLayout.Direction.HORIZONTAL); layout.setDirection(MOBILE, BasicLayout.Direction.VERTICAL); layout.setRelativeWidth(100); layout.setMaxWidth(800); layout.setPadding(10); layout.setCellPadding(10); layout.setGap(10).setGap(MOBILE,20); layout.setCellBorder(new Border()); layout.add(content).setRelativeWidth(33).setRelativeWidth(MOBILE, 100); layout.add(image).setRelativeWidth(33).setRelativeWidth(MOBILE, 100); layout.add(menu).setRelativeWidth(33).setDisplay(MOBILE, false); BasicLayout centerLayout = new BasicLayout(BasicLayout.Direction.VERTICAL); centerLayout.setRelativeWidth(100); centerLayout.add(menu).setHAlign(Align.CENTER).setPadding(20).setDisplay(false).setDisplay(MOBILE, true); centerLayout.add(layout).setHAlign(Align.CENTER); centerLayout.add(footer).setHAlign(Align.CENTER).setPadding(40,0,40,0).setBackgroundColor(new Color("lightgray")); WindowPanel windowPanel = new WindowPanel(); windowPanel.setContent(centerLayout); windowPanel.render(out); } }
Since actions do stuff, it’s a good idea to begin the name with a descriptive verb.
ViewResponsivePage.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class ViewResponsivePage extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ return new ResponsivePage(); } }
Execute the ViewComplexPage action and take a look at the result. Try changing browser width and see what happens.
http://myhost/danceguide/viewResponsivePage
An ajax action is an asynchronous request that typically returns a component that updates an existing element on the page. An ajax action works a lot like standard actions, only we need to state what target element the result should end up in. To achieve this, we give the target element an ID, and we tell the ajax actions to put the result in the element by that ID.
First we create the ajax action. It extracts a message parameter, calculates the number of characters in the message, and returns the result as a component.
AjaxSubmit.java
import org.javadance.basic.*; import org.javadance.basic.ajax.*; import org.javadance.basic.gui.*; public class AjaxSubmit extends AjaxAction{ @Override public void init(){ setActionMapper(AjaxControl.instance.getActionMapper()); } @Override public PushAction perform(AjaxContext context) throws Exception{ String message = context.getRequest().getParameter("message"); String serverName = context.getRequest().getServerName(); int lenght = message.length(); Text resultText = new Text(serverName + " says message is " + lenght + " characters long"); UpdateElement updateElement = new UpdateElement(); updateElement.setElementId(AjaxPage.PANEL_ID); updateElement.setContent(resultText); return updateElement; } }
Then, we create a page containing a form, from which we can submit the ajax action. Note how we set the target element ID for the ajax action. We also create an empty layout cell and give it the same ID.
AjaxPage.java
import java.io.Writer; import org.javadance.basic.gui.*; public class AjaxPage extends GuiComponent{ public static final String PANEL_ID = "result"; @Override public void render(Writer out) throws Exception{ AjaxSubmit ajaxSubmit = new AjaxSubmit(); InputField inputField = new InputField(); inputField.setRelativeWidth(100); inputField.setName("message"); inputField.onKeyUp(new SubmitForm(ajaxSubmit)); VerticalLayout layout = new VerticalLayout(); layout.setPadding(10); layout.setGap(5); layout.setWidth(200); layout.add(new Text("Enter a message")); layout.add(inputField); layout.add(new Space()).setId(PANEL_ID); FormPanel formPanel = new FormPanel(); formPanel.setContent(layout); WindowPanel windowPanel = new WindowPanel(); windowPanel.setContent(formPanel); windowPanel.render(out); } }
Finally, we create a standard action for viewing our ajax page.
ViewAjaxPage.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class ViewAjaxPage extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ return new AjaxPage(); } }
http://myhost/danceguide/viewAjaxPage
When you type in the text field, ajax requests are sent to the server. The server returns a component that is placed in the target element.
WebSockets are used for pushing data from the server to the client asynchronously.
WSControl.java
import javax.websocket.server.ServerEndpoint; import org.javadance.basic.websocket.*; @ServerEndpoint(value="/wsdanceguide", configurator = WebsocketConfigurator.class) public class WSControl extends WebsocketController{ }
WSSubmit.java
import org.javadance.basic.gui.*; import org.javadance.basic.*; import org.javadance.basic.websocket.*; public class WSSubmit extends WebsocketAction{ @Override public void perform(WebsocketContext context) throws Exception{ String message = (String)getParameter("message"); int lenght = message.length(); Text resultText = new Text("Server says message is " + lenght + " characters long"); UpdateElement updateElement = new UpdateElement(); updateElement.setElementId(WSPage.PANEL_ID); updateElement.setContent(resultText); context.sendClientAction(updateElement); } }
WSPage.java
import java.io.Writer; import org.javadance.basic.gui.*; public class WSPage extends GuiComponent{ public static final String PANEL_ID = "result"; @Override public void render(Writer out) throws Exception{ WSControl wsc = new WSControl(); WSControl.OpenStatement openStatement = wsc.getOpenStatement("localhost:8080", false); context.addOnLoad(openStatement); WSSubmit wsSubmit = new WSSubmit(); InputField inputField = new InputField(); inputField.setRelativeWidth(100); inputField.setName("message"); inputField.onKeyUp(new SubmitForm(wsSubmit)); VerticalLayout layout = new VerticalLayout(); layout.setPadding(10); layout.setGap(5); layout.setWidth(200); layout.add(new Text("Enter a message")); layout.add(inputField); layout.add(new Space()).setId(PANEL_ID); FormPanel formPanel = new FormPanel(); formPanel.setContent(layout); WindowPanel windowPanel = new WindowPanel(); windowPanel.setContent(formPanel); windowPanel.setContext(context).render(out); } }
ViewWSPage.java
import org.javadance.basic.gui.*; import org.javadance.basic.http.*; public class ViewWSPage extends HttpAction{ @Override public void init(){ setActionMapper(HttpControl.instance.getActionMapper()); } @Override public GuiComponent perform(HttpContext context) throws Exception{ return new WSPage(); } }
http://myhost/danceguide/viewWSPage
The idea of Dance was borne back in 2001-2002, and originates in a lot of frustration with web development at the time. We wanted object oriented and component based tools for front-end. We got CSS. Layout managements became even worse than using tables.
I had been thinking of some sort of component based system for some time. We were already using Sun Microsystem MVC2 blueprint, but still had to deal with time consuming HTML/CSS.
Or even worse, we had do deal with subcontractors’ and non-programmers’ HTML/CSS. Often, those were art directors at advertising agencies that had become sort of web agencies. Their job was to maintain the HTML/CSS in frontend, so we were stuck with that. To solve this problem, we created a new project model that we called the WebManual. In short, it’s like an extended graphic manual defining all the graphic components in a web application. With the new project model, people who were experts in graphic design developed the WebManual, and we, who had some programming skills, implemented the WebManual in code, even the HTML/CSS. This worked out very well.
Now, we were not bound to HTML/CSS and we could start thinking out of the box. What finally sparked the idea of Dance was an article in Java Developer’s Journal, where a team, of some reason that I don’t remember, genereted some of the HTML/CSS code in a java object. Why not go all the way and let Java components generat all HTML/CSS code, add the components together like in Swing, and render them recursively?
A few days an nights later I had a working prototype. Since all controllers were rendered in a context and could be switched off, it even had decent modal dialogs, which was otherwise not possible at the time.
A few years later we did the first commercial project with Dance. Since then it has been improved over time, and we have had no trouble implementing new web technologies.
Ironically, since I maintain Dance, I have had to become an expert in HTML, CSS and JavaScript, technologies that I tried to get rid of. Maybe WebAssembly will save me in the future. :)
Maintainer Örjan Derelöv orjan@sparkney.se Chef Contributor Erik Derelöv