DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Service-Oriented UI

Michael Schnell user avatar by
Michael Schnell
·
Nov. 15, 12 · Interview
Like (0)
Save
Tweet
Share
7.91K Views

Join the DZone community and get the full member experience.

Join For Free

in large software development projects, service-oriented architecture is very common because it provides a functional interface that can be used by different teams or departments. the same principles should be applied when creating user interfaces.

in the case of a large company that has, among others, a billing department and a customer management department, an organizational chart might look like this:

large company

if the billing department wants to develop a new dialog for creating invoices, it might look like this:

as you can see, the screen above references a customer in the upper part. clicking the “..” button right behind the short name text field will open the below dialog that allows the user to select the customer:

after pressing “select” the customer data is shown in the invoice form.

it’s also possible to select a customer by simply entering a customer number or typing a short name into the text fields on the invoice screen. if a unique short name is entered, no selection dialog appears at all. instead, the customer data is displayed directly. only an ambiguous short name results in opening the customer selection screen.

the customer functionality will be provided by developers who belong to the customer management team. a typical approach involves the customer management development team providing some services while the billing department developers create the user interface and call these services.

however, this approach involves a stronger coupling between these two distinct departments than is actually necessary. the invoice only needs a unique id for referencing the customer data. developers creating the invoice dialog don’t really want to know how the customer data is queried or what services are used in the background to obtain that information.

the customer management developers should provide the complete part of the ui that displays the customer id and handles the selection of the customer:

using jsf 2, this is easy to achieve with composite components. the logical interface between the customer management department and the billing department consists of three parts:

  • composite component (xhtml)
  • backing bean for the composite component
  • listener interface for handling the selection results

provider (customer management departement)

composite component:
<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en"
        "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:composite="http://java.sun.com/jsf/composite"
	xmlns:ice="http://www.icesoft.com/icefaces/component"
	xmlns:ace="http://www.icefaces.org/icefaces/components"
	xmlns:icecore="http://www.icefaces.org/icefaces/core">

<ui:composition>

	<composite:interface name="customerselectionpanel" displayname="customer selection panel" 
	                     shortdescription="select a customer using it's number or short name">
		<composite:attribute name="model" type="org.fuin.examples.soui.view.customerselectionbean" required="true" />		
	</composite:interface>

	<composite:implementation>
	
		<ui:param name="model" value="#{cc.attrs.model}"/>
	
		<ice:form id="customerselectionform">
			<icecore:singlesubmit submitonblur="true" />
			<h:panelgroup id="table" layout="block">

				<table>

					<tr>
						<td><h:outputlabel for="customernumber"
								value="#{messages.customernumber}" /></td>
						<td><h:inputtext id="customernumber"
								value="#{model.id}" required="false" /></td>
						<td> </td>
						<td><h:outputlabel for="customershortname"
								value="#{messages.customershortname}" /></td>
						<td><h:inputtext id="customershortname"
								value="#{model.shortname}" required="false" /></td>
						<td><h:commandbutton action="#{model.select}"
								value="#{messages.select}" /></td>
					</tr>

					<tr>
						<td><h:outputlabel for="customername"
								value="#{messages.customername}" /></td>
						<td colspan="5"><h:inputtext id="customername"
								value="#{model.name}" readonly="true" /></td>
					</tr>

				</table>

			</h:panelgroup>
		</ice:form>

	</composite:implementation>

</ui:composition>

</html>
backing bean for the composite component:
package org.fuin.examples.soui.view;

import java.io.serializable;

import javax.enterprise.context.dependent;
import javax.inject.inject;
import javax.inject.named;

import org.apache.commons.lang.objectutils;
import org.fuin.examples.soui.model.customer;
import org.fuin.examples.soui.services.customerservice;
import org.fuin.examples.soui.services.customershortnamenotuniqueexception;
import org.fuin.examples.soui.services.unknowncustomerexception;

@named
@dependent
public class customerselectionbean implements serializable {

	private static final long serialversionuid = 1l;

	private long id;

	private string shortname;

	private string name;

	private customerselectionlistener listener;

	@inject
	private customerservice service;

	public customerselectionbean() {
		super();
		listener = new defaultcustomerselectionlistener();
	}

	public long getid() {
		return id;
	}

	public void setid(final long id) {
		if (objectutils.equals(this.id, id)) {
			return;
		}
		if (id == null) {
			clear();
		} else {
			clear();
			this.id = id;
			try {
				final customer customer = service.findbyid(this.id);
				changed(customer);
			} catch (final unknowncustomerexception ex) {
				facesutils.adderrormessage(ex.getmessage());
			}
		}
	}

	public string getshortname() {
		return shortname;
	}

	public void setshortname(final string shortnamex) {
		final string shortname = (shortnamex == "") ? null : shortnamex;
		if (objectutils.equals(this.shortname, shortname)) {
			return;
		}
		if (shortname == null) {
			clear();
		} else {
			if (this.id != null) {
				clear();
			}
			this.shortname = shortname;
			try {
				final customer customer = service
						.findbyshortname(this.shortname);
				changed(customer);
			} catch (final customershortnamenotuniqueexception ex) {
				select();
			} catch (final unknowncustomerexception ex) {
				facesutils.adderrormessage(ex.getmessage());
			}
		}
	}

	public string getname() {
		return name;
	}

	public customerselectionlistener getconnector() {
		return listener;
	}

	public void select() {
		// todo implement...
	}

	public void clear() {
		changed(null);
	}

	private void changed(final customer customer) {
		if (customer == null) {
			this.id = null;
			this.shortname = null;
			this.name = null;
			listener.customerchanged(null, null);
		} else {
			this.id = customer.getid();
			this.shortname = customer.getshortname();
			this.name = customer.getname();
			listener.customerchanged(this.id, this.name);
		}
	}

	public void setlistener(final customerselectionlistener listener) {
		if (listener == null) {
			this.listener = new defaultcustomerselectionlistener();
		} else {
			this.listener = listener;
		}
	}

	public void setcustomerid(final long id) throws unknowncustomerexception {
		clear();
		if (id != null) {
			clear();
			this.id = id;
			changed(service.findbyid(this.id));
		}
	}

	private static final class defaultcustomerselectionlistener implements
			customerselectionlistener {

		@override
		public final void customerchanged(final long id, final string name) {
			// do nothing...
		}

	}

}
listener interface for handling results:
package org.fuin.examples.soui.view;

/**
 * gets informed if customer selection changed.
 */
public interface customerselectionlistener {

	/**
	 * customer selection changed.
	 *
	 * @param id new unique customer identifier - may be null.
	 * @param name new customer name - may be null.
	 */
	public void customerchanged(long id, string name);

}

user (billing departement)

the invoice bean simply uses the customer selection bean by injecting it, and connects to it using the listener interface:
package org.fuin.examples.soui.view;

import java.io.serializable;

import javax.annotation.postconstruct;
import javax.enterprise.context.sessionscoped;
import javax.enterprise.inject.new;
import javax.inject.inject;
import javax.inject.named;

@named("invoicebean")
@sessionscoped
public class invoicebean implements serializable {

	private static final long serialversionuid = 1l;

	@inject @new
	private customerselectionbean customerselectionbean;

	private long customerid;

	private string customername;

	@postconstruct
	public void init() {
		customerselectionbean.setlistener(new customerselectionlistener() {
			@override
			public final void customerchanged(final long id, final string name) {
				customerid = id;
				customername = name;
			}
		});
	}

	public customerselectionbean getcustomerselectionbean() {
		return customerselectionbean;
	}

	public string getcustomername() {
		return customername;
	}

}
finally, in the invoice xhtml, the composite component is used and linked to the injected backing bean:

<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en"
        "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:fuin="http://fuin.org/examples/soui/facelets"
      xmlns:customer="http://java.sun.com/jsf/composite/customer">

    <ui:composition template="/web-inf/templates/template.xhtml">
        
        <ui:param name="title" value="#{messages.invoicetitle}" />
    
        <ui:define name="header"></ui:define>
    
        <ui:define name="content">
        	<customer:selection-panel model="#{invoicebean.customerselectionbean}" />
        </ui:define>

        <ui:define name="footer"></ui:define>

    </ui:composition>
    
</html>
summary
in conclusion, parts of the user interface that reference data from other departments should be the responsibility of the department that delivers the data. any changes in the providing code can then be easily made without any changes to the using code. another important benefit of this method is the harmonization of the application’s user interface. controls and panels that display the same data always look the same. every department can also create a repository of its provided user interface components, making the process of designing a new dialog as easy as putting the right components together.

Software development Interface (computing)

Published at DZone with permission of Michael Schnell, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Distributed Stateful Edge Platforms
  • What Is Policy-as-Code? An Introduction to Open Policy Agent
  • Web Application Architecture: The Latest Guide
  • Secrets Management

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: