SCA does not offer an alternative presentation-tier technology. When it comes to user interfaces, SCA’s mantra is integration. Service tiers built with SCA can be integrated with a variety of client technologies, including Swing and rich-clients built using Adobe Flex, AJAX, and web frameworks, such as Struts and Java Server Faces (JSFs).
Instead of a cursory overview of how SCA integrates with a wide variety of presentation-tier technologies, this chapter focuses on how Java EE web applications are used as front-ends to SCA services. Specifically, we cover how servlets and Java Server Pages (JSPs) access SCA services. With this knowledge, it is possible to integrate SCA services with more sophisticated presentation-tier technologies, including the myriad of web frameworks that exist today.
As a component-based technology, it may not be surprising that SCA has the notion of a web component. Web components are Java EE web applications configured as components. What does this mean? In a nutshell, it is the way servlets and JSPs can be wired to SCA services. This brings SCA protocol abstraction benefits to the presentation tier; similar to Java-based components, servlets and JSPs can invoke services without having to resort to low-level, transport-specific APIs.
Web components are like any other SCA component in that they may have references wired to remotable services. Returning to the BigBank loan application from previous chapters, BigBank may decide to offer a consumer-facing web application that offers loans directly to customers. In this case, a Java EE web application will provide the user interface and interact with the LoanService
for processing. Listing 12.1 illustrates this scenario.
Using a web component, implementing a servlet that collects loan application information and submits a request to the LoanService
is fairly straightforward. In fact, the servlet resembles a typical Java-based component, as shown in Listing 12.2.
In Listing 12.2, take note of the @Reference
annotation. As with Java-based components, this instructs the SCA runtime to inject a proxy to a service. When injected, the servlet can invoke the proxy, which may forward the request to a component hosted on a remote runtime. The specific transport used—for example, web services, JMS, or RMI—is conveniently abstracted from the servlet code.
Having seen the essentials of how a web component is implemented, let’s now look in more detail at how one is configured.
In the preceding example, we implemented a servlet with a single reference to the LoanService
. This reference is wired by configuring a web component in a composite. Web components are designated using the implementation.web
element. Listing 12.3 presents the BigBank web component.
As with other component types, when the web component is deployed to a domain, the SCA runtime is responsible for injecting reference proxies—in this case, on the servlet.
Often, more than one servlet in a web component may need to access the same service. In this case, each servlet defines a reference (using the @Reference
annotation) with the same name. In the composite, the reference only needs to be configured once: The SCA runtime will inject a reference proxy into each servlet with an @Reference
declaration. For example, if the BigBank web component contained two servlets with references to LoanService
, the component definition would be the same as in Listing 12.3.
Up to this point, we have not discussed how web components are packaged and deployed to a domain. In Chapter 9, “The Domain,” we detailed how SCA defines a portable packaging format for contributions, namely a ZIP-based archive, but allows for alternative packaging formats. In line with this, web components are packaged as Java EE web archives (WARs). This has several advantages. Most notably, WARs are familiar to most enterprise Java developers. In addition, existing tooling may be used to package web components.
When packaging a web component as a WAR, the composite file that defines the web component must be located in the WEB-INF directory and named web.composite. It is worth noting that in addition to the web component, the composite may define additional components and include other composites. For example, web.composite may configure several Java-based components used by the web application.
In addition to the web.composite file, a WAR-based contribution also must contain an sca-contribution.xml manifest file located in the META-INF directory. As with standard SCA contributions, this manifest file may specify imported contributions, export artifacts, and declare deployable composites. For example, a web component may reference artifacts such as WSDLs or schemas contained in another contribution, in which case it would import that contribution. However, a WAR-based contribution would typically not contain deployable composites other than the one defined in the web.composite file.
One important feature provided by WAR-based contributions is that classes placed in the WEB-INF/classes and jars in the WEB-INF/lib directories are accessible to servlets and JSPs contained in the archive. This is a useful and necessary feature—because Java EE defines this behavior, if SCA did not support it, many web applications would not work when deployed to an SCA runtime.
As we explained in Chapter 9, SCA does not define a standard way to deploy contributions to a domain. A runtime may use a command-line tool, a file directory, a GUI environment, or some other mechanism. However, SCA does define specific behavior for what happens when a composite is deployed to the domain: Components are included as top-level components in the domain composite. Consequently, when web.composite is deployed to a domain, its child components become domain-level components, as illustrated in Figure 12.1.
In the case of the BigBank web component, its reference is wired to the LoanService
offered by the domain-level LoanComponent
. This is shown in Figure 12.2.
Web components may also have properties used for configuration. To access a property, a servlet declares a field or setter with the @Property
annotation. When the web component is instantiated by the SCA runtime, it injects the property values specified in the web component entry.
SCA defines a JSP tag library for accessing services from JSPs. Tag libraries are the standard way defined by Java EE to add custom behavior to JSPs. The SCA tag library contains the reference
tag, which is equivalent to the @Reference
annotation for servlets: It declares a reference, its service contract, and a name for the reference. The JSP fragment in Listing 12.4 demonstrates how this is done.
In the preceding JSP fragment, the <sca:reference>
tag declares the loanService
reference, with a service contract as defined by the bigbank.LoanService
Java interface. Using the tag has two effects. First, it declares a reference for the web component that is wired in the web.composite file. Assuming this JSP and the servlet from the earlier example both declared the loanService
reference, the web.composite file would remain the same as in Listing 12.3. In other words, the web component definition would contain only one <reference>
entry. When the web component is deployed to the domain, the SCA runtime will ensure that reference proxies are available to all servlets and JSPs that declare it. The second effect of the JSP <sca:reference>
tag is that it makes the reference proxy available in the JSP page context using loanService
as the variable name. As seen in Listing 12.4, the reference proxy can be invoked using inline Java.
Generally, using inline Java in JSPs is considered bad practice as it mixes rendering logic (HTML and JavaScript) with application logic. An example that would align more closely with JSP best practices would use other tags to access the reference proxy. Because the reference proxy is made available in the JSP page context, it can be accessed using JSP expressions. For example, assume BigBank has another JSP that displays current rates using RateService
. The current rates returned from the service can be iterated and displayed using built-in JSP tags and the JSP expression language, as shown in Listing 12.5.
To use the reference
tag, you need to include the tag library jar in the web component WAR under the WEB-INF/lib directory. SCA runtimes that support web components (for example, Fabric3) make this tag library available as part of the runtime distribution or development kit. After you have obtained the tag library, it must be declared in a JSP using the taglib
directive, as shown in Listing 12.6.
At times, it is useful to avoid blocking on a request before returning a response to a browser client. In these situations, servlets can use non-blocking operations on SCA services. Assuming the LoanService.apply(..)
operation is marked with the @OneWay
annotation and is long-running, the servlet in the example shown in Listing 12.7 will return a response to the client before the service provider has completed processing.
Asynchronous invocations can improve scalability because the runtime does not need to hold open client connections while processing is being done. This is particularly the case when operations may require a significant amount of time to complete. The main drawback to using asynchronous invocations in servlets is that error handling becomes more difficult. For example, if an error occurs while processing the loan, the user will not receive immediate feedback because the servlet will have already returned a response to the client. Instead, error handling would need to be done by LoanComponent
(that is, the component providing LoanService
), with possibly a notification sent to the user via email or some other communications channel.
Although servlets and JSPs may invoke non-blocking operations, they cannot receive callbacks. If a service is bidirectional (that is, it specifies a callback service), it must be accessed from an intermediary service that implements the required callback interface. To understand how this works, let’s return to the CreditService
callback example we introduced in Chapter 3, “Service-Based Development Using Java.” The CreditService
and CreditServiceCallback
interfaces are defined in Listing 12.8.
Because the CreditService
requires the client to provide a callback service, it cannot be invoked from a servlet. Instead, an intermediary service would need to be wired to the servlet, which in turn would have the CreditService
wired to it. The intermediary would be responsible for handling the callback. The SCDL for setting this up is provided in Listing 12.9.
Up to this point, we have discussed how to access non-conversational services from servlets and JSPs. Doing so is fairly straightforward, particularly because client servlets or JSPs do not need to take threading issues into account, even though web components are by nature multithreaded—that is, they may receive more than one simultaneous request. If the SCA service accessed by a servlet or JSP is implemented by a stateless component, the SCA runtime will guarantee that only one web request will have access to it at a time, as illustrated in Figure 12.3.
In this case, the servlet or JSP and component implementation does not need to take any special care, such as synchronizing field access. When a service is implemented by a composite-scoped component, it is up to it to manage concurrent access (perhaps by not using field variables or synchronizing access to them), as all requests will be dispatched to the same instance (see Figure 12.4).
When invoking a service provided by a composite-scoped component, the servlet or JSP does not need to regulate concurrent access because the service provider handles it internally in the component implementation.
If a service is conversational, servlets and JSPs will need to take special care with concurrent access. To guard against inadvertently having two or more clients access the same conversational service instance through a servlet or JSP, web components associate references wired to conversational services with the HTTP session. This means that conversational services cannot be injected on a servlet field using the @Reference
annotation. For example, assuming that MyConversationalService
is marked as @Conversational
, the code in Listing 12.10 will result in an error when the contribution containing the web component is installed.
If the preceding code were legal, because servlets handle multiple simultaneous requests, it would result in every client accessing the same instance of the MyConversationalService
instance—something that is most likely not intended.
There are two options for accessing conversational services from a servlet. The first is to use the ComponentContext
API, as shown in Listing 12.11.
The ComponentContext
instance—which is threadsafe—is injected on the servlet using the @Context
annotation. When the ComponentContext.getService(..)
API is called, passing the expected interface type and reference name, a reference proxy is returned. This proxy will always dispatch invocations to the same instance for the current HTTP session. If two requests associated with different HTTP sessions are received by the servlet, the code in Listing 12.11 will dispatch to two different instances of the MyConversationalService
. If, however, two requests associated with the same HTTP session arrive, they will be dispatched to the same instance. Figure 12.5 illustrates this dispatching.
The second option for accessing a conversational service is to use the Servlet HttpSession
API. Listing 12.12 shows how this is done.
As seen in Listing 12.12, the SCA runtime makes reference proxies available from the HTTP session by reference name. Which way is better: using ComponentContext
or HttpSession
API? Whatever method is chosen is largely a matter of personal preference. The important thing to remember is not to attempt to store reference proxies in member variables, as doing so will expose a conversational instance to all web component clients.
Previously, we mentioned that the SCA runtime will scan the contents of a web component for @Reference
annotations and reference
JSP tags when it is installed to determine the set of wireable references.
What happens when there are no @Reference
annotations or reference
tags used in a web component? This could happen if a web component contains servlets that only use the ComponentContext
API. In this case, the runtime has no way to determine the set of references for a web component by introspecting its servlets and JSPs.
When this happens, a special file called a web.componentType file must be created and placed in the WEB-INF directory of the WAR. The componentType file is an XML file that defines services, references, and properties for a component implementation. We haven’t mentioned the componentType file previously because it is generally not needed—in Java, annotations can be used instead.
An example of a web.componentType file that defines a single reference is shown in Listing 12.13.
It is possible to use a combination of @Reference
annotations, reference
JSP tags, and a web.ComponentType file. The SCA runtime will combine all three sources when calculating the wireable references for a web component.
This chapter has provided an introduction to integrating SCA services with presentation tiers built using Java EE web applications. It has covered using web components to wire from servlets and JSPs to services. With this knowledge, you should have a thorough understanding of the basics to build user interfaces that front SCA services.