XBean QuickStart Guide |
A Guide to Getting Started With XBean
Introduction
Introduction
This guied will lead you through the process of installing XBean, and using it effectively. This distribution has a "sandbox" environment that makes it easy to get started with XBean without stumbling on mundane environmental issues. Once you become comfortable with the basics, you can copy the JAR files to your own project and begin work.
Installing XBean
Download
Download the latest release of j2Xtreme/XBean.
Unpack the Distribution
Each distribution contains pre-compiled binary JAR files as well as the source code. When you unpack the j2xtreme distribution, you should have a directory tree that looks something like this. For the purposes of this quickstart guide, we will refer to this directory as $J2XTREME_HOME
$J2XTREME_HOME |-- README.html |-- build.cmd |-- build.properties |-- build.sh |-- build.xml |-- docs | |-- faq.html | |-- images | |-- index.html | |-- skin | |-- xbean | `-- xwidget |-- j2xtreme-xbean-0.1.0.jar |-- j2xtreme-xwidget-0.1.0.jar |-- lib | |-- commons-beanutils-1.6.1.jar | |-- commons-collections-3.0.jar | |-- commons-dbcp-1.1.jar | |-- commons-lang-2.0.jar | |-- commons-logging-1.0.3.jar | |-- commons-pool-1.1.jar | |-- ext | `-- jakarta-oro-2.0.8.jar |-- xbean | |-- build | |-- build.xml | |-- examples | `-- src `-- xwidget |-- build |-- build.xml |-- src |-- temp `-- webapps
NOTE: It is helpful but not necessary to have ANT installed on your machine and in your PATH. In case you don't, the j2Xtreme distribution contains a version of ant that you can use. In the $J2XTREME_HOME directory there are two shell scripts build.sh and build.cmd that will invoke ant on Unix/Linux/Cygwin and Windows respectively. The rest of this tutorial assumes that you can invoke ant 1.6.1+ using either the installed version or by using build.sh/build.cmd.
Architecture Overview
Design Philosophy
XBean is a microkernel service container that is responsible for making named services available to your application while hiding the details of object instantiation and lifecycle. Unlike EJBs (probably the most well-known of all service oriented architectures) XBean is designed to be extremely lightweight and non-distributed.
Non-distributed means that it is entirely intra-JVM, no effort for inter process communication is intended. We believe that network transparency in terms of application design is a dream that is unlikely to be fulfilled until/unless network throughput improves several orders of magnitude from current standards. Providing a framework that claims: "Use me and your application will be high-performing, flexible, easy to maintain, and reliable" is like giving someone a loaded gun. That is not to say that these goals aren't achievable. However, in the real world with constraints on funding, resources, talent and schedule, such claims will fall short 80-90% of the time.
Lightweight means that 1) it should impose minimal performance and resource overhead on your application and 2) that it should be minimally intrusive to your application design. The latter is critically important, because we don't want your object model to be specific to your needs. We don't want you to have to shoehorn your object model into our architecture. We do, however, feel that the container should facilitate great design.
Quite often programmers are faced with a design choice that places good design against efficiency (and laziness). We believe that this is fundamentally a false choice and is, in many respects a result of working with an inadequate framework and/or one that is excessively cumbersome (read: EJB). So, for instance, when faced with that pesky question: "Should I just hard-code this configuration parameter for now...?" we would like j2Xtreme/XBean to make the answer to that question to be unambiguous. More importantly, we would like the anser to "Should I design this in a manner that can be reconfigured, enhanced, and retooled in the future?" to be a resolute yes. Good design should be EASY!
XBean leverages the JavaBean component model wherever possible. The JavaBean model is a really fantastic component model. It is clean, simple, effective and not tied to design-time tools as is, say, COM. A JavaBean is really nothing more than a plain old java object with a public constructor with public get/set methods. What Sun has really declined to offer in J2SE or J2EE is a true framework for instantiation, configuration, and lifecycle management. j2Xtreme/XBean is here to fill that gap.
By being component- and service-oriented, XBean encourages a kind of tinkertoy approach to building applications. Components can be wired together easily, but they can be re-wired and re-configured just as easily to meet the task at hand, often.
XBean does not dictatorial about the design patterns that you use in your application. Although it uses a named service model, you are free to use those named services any way you like. We think that this is a distinct advantage over some of the more bleeding edge microkernel containers that use dependency injection. While I happen to like the idea of dependency injection, I tend to believe that it will never be understood by the majority of commercial/enterprise software developers. To that end, XBean aims to be so simple that it's impossible to **** up.
But enough pontification, let's see it work....
Tutorials
The source code for the following tutorials is available in $J2XTREME_HOME/xbean/examples. There are also ANT build scripts available to make it very simple to get started.
Hello World
Hello World, the old way...
HelloWorld is admittedly trivial, but it is very effective at making a point. A typical java HelloWorld application would look something like:
package helloworld; import com.j2xtreme.xbean.*; public class HelloWorld { public void sayHello() { System.out.println("Hello, World!"); } public static void main(String args[] ) { HelloWorld hello = new HelloWorld(); hello.sayHello(); } }
It is simple enough, but problems with it emerge almost immediately. What if, for instance, I want to extend HelloWorld to enhance the sayHello() method, but don't want to (or can't) change the main() method.
HelloWorld, the XBean way
Here is an equally trivial HelloWorld application, this time using XBean:
package helloworld; import com.j2xtreme.xbean.*; public class HelloWorld { public void sayHello() { System.out.println("Hello, World!"); } public static void main(String args[] ) { HelloWorld hello = (HelloWorld) ServiceContext.lookup("/GreetingService"); hello.sayHello(); } }
OK, so you're asking how does XBean know to translate the lookup request for /MyHelloWorldService to the proper instance.
XBean uses a very lightweight deployment descriptor, as it were, named ServiceConfig.xml that goes in your CLASSPATH with the relative path META-INF/ServiceConfig.xml. (Note: This is just one of the ways that XBean loads its configuration...see below for more.
META-INF/ServiceConfig.xml:
<services> <service name="/MyHelloWorldService" class="mypackage.HelloWorld" /> </service> </services>
That's it...pretty simple, eh? I probably don't even need to explain, though I will. Calling ServiceContext.lookup causes the XBean container to search its configuration for an XBean Service named /MyHelloWorldService. It needs a concrete class to instantiate, and this is defined by the class attribute of the corresponding service entry. Internally, the container does this resolution and calls Class.forName().newInstance().
OK, you're yawning...let's take the next step.
Extending HelloWorld the XBean way...
Now pretend that you want to enhance HelloWorld to be able to say hello to whomever you like, but for whatever reason, you editing the source code isn't an option. With XBean, you can do something like this:
package helloworld; import com.j2xtreme.xbean.*; public class ConfigurableHelloWorld extends HelloWorld{ String myName="World"; public void setName(String name) { myName = name; } public String getName() { return myName; } public void sayHello() { System.out.println("Hello, "+myName+"!"); } }
And you changed ServiceConfig.xml to the following. (Note: XBean is carefully designed so that you don't have to edit configuration files when you're really really trying to override configuration. However, this is a more advanced topic that is covered later.
<services> <service name="/MyHelloWorldService" class="mypackage.Hello" /> </services>
Now when you run HelloWorld, it will actually be the class mypackage.Hello that is loaded and invoked. This seems simple enough, but a very critical thing has happened here. Without modifying the exisiting source code, you were able to enhance and make the orginal program more generic and more flexible. By using XBean with original HelloWorld, you unwittingly future-proofed it with little or no extra effort!
But now it is possible to configure the application to say hello in a configurable manner. By changing ServiceConfig.xml slightly:
<services> <service name="/MyHelloWorldService" class="mypackage.Hello" > <property name="name">George Orwell</property> </service> </services>
The HelloWorld application will now print:
Hello, George Orwell!
XBean automatically finds and sets the name JavaBean property with the correct value. There is no fussing with properties files, resource bundles, class loaders, URLs, type casting, key names, etc. XBean supports automatic type-casting and configuration for all primitive types and primitive wrappers.
HelloWorld with a dependency graph
The above is all well and good, but the real power of XBean enters when complex Object types are used to wire together complex graphs of objects.
Consider the case where we want the person we are saying "Hello" to in our application, to be described by an object. Let us consider a Person bean class that has properties for firstName and lastName. Thus we might have a class:
package helloworld; public class Person { String myFirstName=""; String myLastName=""; public String getFirstName() { return myFirstName; } public String getLastName() { return myLastName; } public void setFirstName(String name) { myFirstName = name; } public void setLastName(String name) { myLastName = name; } public String toString() { return myFirstName+" "+myLastName; } }
And we extend the original HelloWorld to HelloDependency:
package helloworld; public class DependencyHelloWorld extends HelloWorld { Person myPerson = new Person(); public void setPerson(Person p) { myPerson = p; } public Person getPerson() { return myPerson; } public void sayHello() { System.out.println("Hello, "+getPerson()+"!"); } }
We can now wire our instance of HelloDependency to an instance of Person:
<services> <service name="/MyHelloWorldService" class="mypackage.HelloDependency" > <property name="person">xbean:/PersonToGreet</property> </service> <service name="/PersonToGreet" class="mypackage.Person"> <property name="firstName">Jerry</property> <property name="lastName">Garcia</property> </service> </services>
And when we run the original program, we get:
Hello, Jerry Garcia!
Pretty cool!. When we requested /MyHelloWorldService, XBean sees that the JavaBean property person is an object and that we are requesting it to be initialized with an XBean reference, xbean:/PersonToGreet. XBean then resolves this dependency and instantiates /PersonToGreet and assigns it to the person property of MyHelloWorldService.
For the skeptics: Yes, XBean will resolve complex and circular dependencies!
Advanced Topics
Scoping
One of the most powerful aspects of XBean is its concept of scoped services. Every name XBean Service has an associated scope. The best way to understand scope is in terms of the conditions under which two distinct calls to ServiceContext.lookup() returns an identitical reference. That is, what are the condutions under which:
ServiceContext.lookup("/MyBean")==ServiceContext.lookup("/MyBean")
The scopes supported by XBean and respective rules for indentity are:
Scope Name | Identity Rule |
---|---|
transient | Never. Two lookups will never return the same reference just as two calls to Class.newInstance() will never return the same reference. |
global | Always. global has the same semantics as a singleton. |
thread | Different threads must never return the same reference. The same thread is guaranteed to return the same reference unless ServiceContext.unbindThread() has been called between two calls to lookup(). |
application | Always, when the current thread is executing within the same ServletContext. |
session | Whenever the current thread is executing within the same HttpSession. Calls to lookup() from different HttpSessions are guaranteed to return different references. |
request | Whenever the current thread is executing within the same Request. Different requests are guaranteed to return different references |
widget | More or less the same as Session, but specifically designed for XWidget. |
These scoping rules end up being quite powerful because the the enclosing scope is implicitly managed by the XBean container. For instance, thread scope doesn't do anything that couldn't be accomplished with java.lang.ThreadLocal, except that you don't need to pass the reference to the ThreadLocal around. The benefit should be abundantly clear to anyone who's done Servlet or JSP programming and wanted to do functional decomposition, but not wanted to pass HttpServletRequest and HtppServletResponse as parameters and for which making them members is not an option. With XBean, objects with request scope are always available indirectly so long as the current thread is executing within an Servlet request. In fact, it is possible to obtain the standard servlet objects at any time within the context of a webapp using built-in services:
ServiceContext.lookup("/javax/servlet/ServletContext"); ServiceContext.lookup("/javax/servlet/http/HttpSession"); ServiceContext.lookup("/javax/servlet/http/HttpServletRequest"); ServiceContext.lookup("/javax/servlet/http/HttpServletResponse");
How do you demarcate the boundaries for application, session, request scopes? You are free to do it manually, but it is HIGHLY advised to install a ServletFilter that comes with XBean, which will pretty much guarantee that the correct objects are bound.
One of the most useful things about XBean is that you can register a listener with the container which will be alerted when the corresponding service has gone out of scope. This is tremendously useful in terms of, say, ensuring that dynamically allocated resources are guaranteed to be cleaned up. for instance, it is possible to ensure that JDBC connections are always committed or rolled back and returned to their DataSource pool.
Custom Factories
Explanation of using XBean with classes that aren't JavaBeans...