Click here to Skip to main content
15,867,594 members
Articles / Programming Languages / Java / Java SE / J2EE

Modular J2EE, Using Spring DM, Zk and EclipseLink

Rate me:
Please Sign up or sign in to vote.
4.92/5 (9 votes)
21 Apr 2011CPOL15 min read 52.4K   401   8   12
This article describes how to create a basic modular Java Enterprise Application, by using these Open Source frameworks

Contents

Introduction

Let's play! Imagine playing an action game like God of War(0) with the difficulty level set to "Nightmare." Now, can you imagine beating the final boss "Zeus" easily? Maybe if the game had a bug or if were cheat you could imagine winning, but the process would be slow-going! How can we battle him without passing all the missions?

This is impossible; in order to face Zeus on Nightmare difficulty we need to pass all the missions from the first to the last. And in this article, we will do just: We play the first mission of the J2EE level, but we will play it on Nightmare difficulty.

This article describes how to create a basic modular Java Enterprise Application, by using these Open Source frameworks: an OSGi implementation, Spring, Zk, EhCache, EclipseLink, and some others. I will keep things as simple as I possibly can. But! where is this "nightmare" and this dramatic story? You will be able to answer this question by yourself later.

Ah! I forget to tell you, the Zk Framework doesn't have support for OSGi or EhCache =). No worries, you will taste them. Another thing, just because I said "Zk," and "EclipseLink," doesn't mean I favor one framework over another because this article doesn't favor anything. It is a challenge, a challenge to put certain frameworks in action, nothing more.

You might be saying, "This sound interesting, but I'm a newbie. What is 'OSGi' and 'EhCache' and ...?" Do a little research on them first and come back. There is a full section here just for making a clear image of all these things in your mind.

In your Mind

If you really do the research on OSGi, or you know it already, or you have simply heard by OSGi(1), you can go off this small description:

"OSGi, euh, it is just a framework that allows us to create modular applications, so we can add/ remove plugins easily, and applications stay working without any problems. Experts call these plugins bundles, and it is similar to Eclipse"

With this interesting framework we can be more organized in our development, but don't forget Eclipse is our "Kitchen." But what about the rest of our ingredients like Zk(2)?:

"hmm... Zk! is a web framework for Java, similar to Adobe Flex. However, Zk don't use Flash or MoonLight but generates HTML. Wait, wait, it is like ASP.Net with Ajax Toolkit."

Yeah, cool, Drag and Drop, Effects, no Postback, etc..., very nice RIA. Next is Spring and Spring DM(4,5):

"Spring and Spring DM, just all in one (or in two?)"

Firstly this is a slogan not a description. Secondly, yes; it has many modules for maybe everything needed to develop a Java applications (desktop or web). Last but not least is EclipseLink and EHCache (6,7):

"EclipseLink is an ORM framework for persistence, for inserting, updating, etc... and it is powerful that classical JDBC. EhCache is what his name implies, used for caching things (objects) in memory or hard disk."

Additionally, EclipseLink supports OSGi by default, unlike EhCache which, ifwe battle with it, can support OSGi. There are other secrets about these frameworks but we will discover them later.

Now the big picture in your mind is like ... TA-DA:

image001.jpg

No worries at all, this just game we assemble it here, for we can finally have something like this or like:

image002.jpg

It is wonderful to hear (read) "Great! You have finished the first mission of J2EE Level: Nightmare!" This also means we can now dirty our hands in Kitchen (Environment) setup, after closing this visual novel.

Environment Setup

Do you remember how we mentioned in precedent chapters the word "Eclipse?" Now it is time to set it up using easy steps for everyone so we can be familiarized with this new world (ops kitchen).

Don't download them integrated by default, use "UPDATE SITE INSTALLATION" so we can have all these tools in one environment. Or if you prefer to discover these toools do whatever you want — just don't lose your way.

Finally we have our environment which looks like this:

image003.gif

We can see Zk things and in top left "Spring Elements."

Now to create our platform for OSGi development in Eclipse, we go to Menu Window > Preferences > and Target Platform. After that we create new target called "J2EE Nightmare" and we add all JARs that support OSGi, this means JARs that we have properties in there MANIFEST files for can be visible in OSGi world, for example:

image004.jpg

(Target creation)
 
[START-CODE]
Manifest-Version: 1
Ant-Version: Apache Ant 1.8.1
Bnd-LastModified: 1302002802162
Bundle-ManifestVersion: 2
Bundle-Name: zk
Bundle-SymbolicName: zk
Bundle-Version: 0
Created-By: 1.6.0_20 (Sun Microsystems Inc.)
Export-Package: metainfo.mesg,metainfo.tld,metainfo.zk,org.zkoss.zk,org...
[END-CODE]

You notice something? No? Scroll up and re-read "I forget to tell you, the Zk Framework doesn't have support for OSGi" and now we see the MINEFIST file for Zk contains OSGi declarations! What does this mean? Yup, I have added it for you, just download all JARs and add them. To differentiate these JARs I have added the prefix "adrabi-osgi-" if you wish change them by your own.

You can download all these JARs from here: https://sourceforge.net/projects/modularj2ee/files/platform/ the zip file called platform_1.0.0.zip.

Testing Time! We want to create a new OSGi run configuration and we UNCHECK three bundles:

  • com.springsource.junit
  • org.springframework.test
  • org.springframework.osgi.test

These are the testing bundles and also, your homework :D

image005.gif

Also, we check "Clear the configuration area before launching" in Settings. Now push the button "Run" after 1 second and you will see a very nice red warning:

[BEGIN-CODE]
log4j:WARN No appenders could be found for logger (org.springframework.osgi.web.tomcat.internal.Activator).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[END-CODE]

If you come come back in a hurry to find out what you have missed just calm down because this is normal warning which we will fix before starting.

Before Starting

We start by creating a fragment for Log4j (fragment is a part of bundle).

Logging Fragment

We go to menu New > Project ... > Plug-in Development > Fragment Project.

image006.gif

We give to project a name and choose "an OSGi framework." After that (do you remember?) "fragment is a part of bundle," we need a host bundle! It is log4j "com.springframework.apache.log4j" (click in browser button and type "com" for you can list them):

image007.gif

In this version we remove ".qualifier." Then we create a file named log4j.properties in the root of this project with this simple configuration:

[BEGIN-CODE]
# show log in console
log4j.rootLogger=info, A
log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n      
[END-CODE]

We run again our OSGi configuration and we see something like:

[BEGIN-CODE]
osgi> 0    [Tomcat Catalina Start Thread] INFO  org.springframework.osgi.web.tomcat.internal.Activator 
 - Starting Apache Tomcat/5.5.23 ...
       1    [Tomcat Catalina Start Thread] INFO  org.springframework.osgi.web.tomcat.internal.Activator  
 - Using default XML configuration bundleresource://29.fwk7579563/conf/default-server.xml
       75   [Start Level Event Dispatcher] INFO  org.springframework.scheduling.timer.TimerTaskExecutor  
 - Initializing Timer
...
[END-CODE]

Sayonara Red warning =) We can jump quickly to configuring the validation too.

Validation Fragment

We create another fragment for bundle "com.springsource.javax.validation," but now we have to do something more complex. We also have to create some directories in META-INF:

  • services folder with file named javax.validation.spi.ValidationProvider contains org.hibernate.validator.HibernateValidator.
  • spring folder with two files jsr303-services-cfg.xml for spring beans and jsr303-osgi-cfg.xml osgi services.

image008.jpg

Also, things become more interesting, and we create a Proxy (read more about Proxy in design pattern) for default Validation factory "JSR-303" like this:

C#
[BEGIN-CODE]
public class ValidatorFactoryProxy implements ValidatorFactory
{
       private ValidatorFactory factory = null;
       
       public ValidatorFactoryProxy()
       {
             this.factory = Validation.buildDefaultValidatorFactory();
       }
 
       @Override
       public Validator getValidator()
       {
             return this.factory.getValidator();
       }
 
       @Override
       public ValidatorContext usingContext()
       {
             return this.factory.usingContext();
       }
 
       @Override
       public MessageInterpolator getMessageInterpolator()
       {
             return this.factory.getMessageInterpolator();
       }
 
       @Override
       public TraversableResolver getTraversableResolver()
       {
             return this.factory.getTraversableResolver();
       }
 
       @Override
       public ConstraintValidatorFactory getConstraintValidatorFactory()
       {
             return this.factory.getConstraintValidatorFactory();
       }
 
       @Override
       public <T> T unwrap(Class<T> type)
       {
             return this.factory.unwrap(type);
       }
}
[END-CODE]

Another secret you might have noticed is we use Hibernate Validation. We missed just one more thing which is to add:

[BEGIN-CODE]
 
Require-Bundle: com.springsource.org.hibernate.validator;bundle-version="4.1.0"
 
[END-CODE]

in MANIFIST file, we move on to to forging a core component.

Core Component

We keep the core very simple. It is just for configuring basic services that will be used in all applications:

  • We configure data source service and transactions.
  • Also, cache service.

We create now a bundle not a fragment, called in Eclipse "Plug-in Project" (remember to choose the target platform as "an OSGi framework"), give our project a name, and finally we create two directories in the META-INF folder called "config" and "spring,"

  • "config" folder contains: configurations files for ehcache and jdbc.
  • "spring" folder contains: configurations files for spring beans and OSGi services.

We well show some files here and others you can download project example and browse them:

confg/jdbc.properties file

[BEGIN-CODE]
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc\:hsqldb\:mem\:jlnDatabase
jdbc.username=sa
jdbc.password=
jdbc.databasePlatform=org.eclipse.persistence.platform.database.HSQLPlatform
loadTimeWeaver.weaver=org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver
[END-CODE]

spring/core-data-cfg.xml file

[BEGIN-CODE]
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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 <a href="http://www.springframework.org/schema/context">http://www.springframework.org/schema/context</a> http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
 
       <!-- Config properties  -->
       <context:property-placeholder location="classpath:/META-INF/config/jdbc.properties"/>
 
       <!-- Config data source -->
       <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
           init-method="createDataSource" destroy-method="close">
             <property name="driverClassName" value="${jdbc.driverClassName}"></property>
             <property name="url" value="${jdbc.url}"></property>
             <property name="username" value="${jdbc.username}"></property>
             <property name="password" value="${jdbc.password}"></property>
       </bean>
 
       <!-- Config entity manager -->
       <bean id="entityManagerFactory" 
           class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
             <property name="dataSource" ref="dataSource"></property>
             <property name="jpaVendorAdapter">
                    <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
                           <property name="databasePlatform" value="${jdbc.databasePlatform}">
                           </property>
                    </bean>
             </property>
             <property name="loadTimeWeaver">
                    <bean class="${loadTimeWeaver.weaver}"></bean>
             </property>
             <property name="jpaProperties">
                    <props>
                           <prop key="eclipselink.logging.level">FINEST</prop>
                           <prop key="eclipselink.ddl-generation">create-tables</prop>
                    </props>
             </property>
             <property name="persistenceXmlLocation" 
                  value="classpath*:/META-INF/persistence/**/persistence.xml"></property>
       </bean>
       
       <!-- Config transaction -->
       <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
             <property name="entityManagerFactory" ref="entityManagerFactory"></property>
       </bean>
</beans>
 
[END-CODE]

This configuration for using HSQL(8) as a database with "pooling" by using the DBCP(9) Component, note it has another secret and lets us continue our work in service component.

Service Component

We type a delicious code for crafting our component service, transactions, cache services, and also an interface for our services and implementation. We're also going to need some entities! We will do so quickly in the next section "Bored Story."

Bored Story: Domain

As we motioned, we'll do this quickly. I'm also too lazy to think of and type a BIG entity. For this reason we will use a ready entity from this article: http://www.vogella.de/articles/JavaPersistenceAPI/article.html. No need to guess what is!

Okay, we create another bundle for the entities component, we call it "jln-entities" and we create the entity Todo:

C#
[BEGIN-CODE]
@Entity
public class Todo implements Serializable
{
       
       /**
        *
        */
       private static final long serialVersionUID = 1L;
       
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
       
       @Size(min=10, max=100)
       @Pattern(regexp="^#[\\w+\\s]+")
       private String summary;
       
       @Size(min=10, max=500)
       private String description;
 
       public Long getId()
       {
             return id;
       }
       
       public String getSummary() {
             return summary;
       }
 
       public void setSummary(String summary) {
             this.summary = summary;
       }
 
       public String getDescription() {
             return description;
       }
 
       public void setDescription(String description) {
             this.description = description;
       }
 
       @Override
       public String toString() {
             return "Todo [summary=" + summary + ", description=" + description
                           + "]";
       }
}
[END-CODE]

This is all, just notice "@Size,@Pattern" for validation. Next, we create a fragment for core component called "jln-persistence-1.0.0," which contains two directories 1.0.0 in "persistence," in META-INF folder. Remember this line in the core-data-cfg.xml file:

XML
[BEGIN-CODE]
<property name="persistenceXmlLocation" value="classpath*:/META-INF/persistence/**/persistence.xml"></property>
[END-CODE]

We put the file called persistence.xml in folder 1.0.0 with very simple configuration:

XML
[BEGIN-CODE]
<?xml version="1.0" encoding="UTF-8" ?
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation=
"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
       version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
       <persistence-unit name="jln" transaction-type="RESOURCE_LOCAL">
             <class>com.cp.adrabi.jln.model.Todo</class>
       </persistence-unit>
</persistence>
[END-CODE]

Finished! We back to our service, for CRUD Todo after exporting package "com.cp.adrabi.jln.model," we add this line in the MANIFEST file for bundle “jln-entities”:

[BEGIN-CODE]
 
Export-Package: com.cp.adrabi.jln.model
 
[END-CODE]

Finally our Todo service implementation is completed too, by adding cache annotations:

C#
[BEGIN-CODE]
@Service("todoService")
@Transactional
@Repository
public class TodoServiceImpl implements TodoService
{
 
       private final static String SELECT_ALL_TODO = "select t from Todo t";
       
       @PersistenceContext
       private EntityManager entityManager;
       
       @Override
       @Cacheable(cacheName="todoCache")
       public Todo getTodoById(Long id)
       {
             return this.entityManager.find(Todo.class, id);
       }
 
       @Override
       @Cacheable(cacheName="todoCache")
       public List<Todo> getListTodo()
       {
             return this.entityManager.createQuery( SELECT_ALL_TODO, Todo.class).getResultList();
       }
 
       @Override
       @TriggersRemove(cacheName="todoCache",removeAll=true, when=When.AFTER_METHOD_INVOCATION)
       public void saveTodo(Todo todo)
       {
             this.entityManager.persist(todo);
       }
 
       @Override
       @TriggersRemove(cacheName="todoCache", removeAll=true, when=When.AFTER_METHOD_INVOCATION)
       public void updateTodo(Todo todo)
       {
             this.entityManager.merge(todo);
       }
 
       @Override
       @TriggersRemove(cacheName="todoCache", removeAll=true, when=When.AFTER_METHOD_INVOCATION)
       public void removeTodoById(Long id)
       {
             Object tmp = this.entityManager.getReference(Todo.class, id);
             this.entityManager.remove(tmp);
       }
}
[END-CODE]

The rest is just Spring configuration files and MANIFEST files too. Remember NEVER forget to look in the MANIFEST file for ALL these bundles and fragments because they contain importation/exportation for many, many, many packages and bundles.

But we are not sure this service work OK! To be sure that service will work and connect to a database etc... we need to do some homework for testing.

Homework: Testing

For preparing testing environment we need to create a user library in menu Window > Preferences > Java Tab > Build Path Tab > User Libraries, we give it a name and we add all JARs.

image009.gif

At last! We've created a Java project. "Java Project" is not Plug-in nor Fragment, but a classical Java project and we add to it our user library (don't forget to add projects entities and todo component to this project in tab "Projects").

image010.gif

We create the integration test extends class AbstractConfigurableBundleCreatorTests and we override two methods:

  • Resource[]:getTestFrameworkBundles for setup our custom bundles.
  • And String:getRootPath to override the default class path.

Now we forge some private stuff, for getting services from OSGi. Remember? The services that we have registered by using Spring OSGi look in spring configuration, core, and todo components!

makeService() method for getting registered Todo service from OSGi:

C#
[BEGIN-CODE]
private TodoService makeService()
{
	ServiceReference ref = this.bundleContext.getServiceReference("com.cp.adrabi.jln.todo.services.TodoService");
	return (TodoService) this.bundleContext.getService(ref);
}
[END-CODE]

makeCache() method for getting registered Cache service from OSGi:

C#
[BEGIN-CODE]
private CacheManager makeCache()
{
	ServiceReference ref = this.bundleContext.getServiceReference("net.sf.ehcache.CacheManager");
	return (CacheManager) this.bundleContext.getService(ref);
}
[END-CODE]

Other stuff you discover in its source and now we create a test for three basic objects: bundleContext, todoService and cacheService. If these three objects don't work, we've done all this for nothing =)

C#
[BEGIN-CODE]
public void testBundleContext()
{
	assertNotNull( this.bundleContext );
}
       
public void testTodoService()
{
	assertNotNull( makeService() );
}
 
public void testCacheService()
{
	assertNotNull(makeCache());
}
[END-CODE]

Before you run this test, we need to export all our bundles/fragments:

image011.gif

This is the final screen-shot for all tests. You need to change the path for the JARs folder on your own, the path in the screen-shot is in Linux and you change it by c:/... or if you are in Windows, the total tests in this test for integration is 10 with tests for the cache included too.

image012.gif

Another thing to note is that this works only with JUnit 3. In other words AbstractConfigurableBundleCreatorTests == Green if and only if JUnit.version == 3.

print “Time to craft presentation tier starting with Web Core component”;

Web Core Component

Web core component is a bundle. We need to create a Plug-in project, give it a name configure it in three parts: "MVC," "Security," and "URL rewriting." But web bundles have different structures than other bundles, and we will show this in the MVC configuration.

Bored Story Again: MVC

The first thing we do now is adding this line "Web-ContextPath: /" in the MANIFEST file for this bundle. What does this line mean? By adding this line, the URL of our web application be like this: “http://127.0.0.1:8080”, we don't need extra path in the URL like "http://127.0.0.1:8080/jln-blabla/".

What is MVC? Rather "How does our web structure look?" Like this:

image013.jpg

We have created a new folder called WEB-INF. In it two other folders components and spring, in web.xml we add this configuration for loading the spring configuration for the web component by Spring Dynamic Modules:

XML
<context-param>
             <param-name>contextClass</param-name>
             <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
       </context-param>
       <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>/WEB-INF/spring/*.xml</param-value>
       </context-param>
[END-CODE]

Also this configuration for supporting and using Zk framework:

XML
[BEGIN-CODE]
<listener>
             <description>Used to cleanup when a session is destroyed</description>
             <display-name>ZK Session cleaner</display-name>
             <listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
       </listener>
       <servlet>
             <description>The ZK loader for ZUML pages</description>
             <servlet-name>zkLoader</servlet-name>
             <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
             <init-param>
                    <param-name>update-uri</param-name>
                    <param-value>/zkau</param-value>
             </init-param>
             <load-on-startup>1</load-on-startup>
       </servlet>
       <servlet>
             <description>The asynchronous update engine for ZK</description>
             <servlet-name>auEngine</servlet-name>
             <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
       </servlet>
       <servlet-mapping>
             <servlet-name>zkLoader</servlet-name>
             <url-pattern>*.zul</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
              <servlet-name>zkLoader</servlet-name>
             <url-pattern>*.zhtml</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
             <servlet-name>auEngine</servlet-name>
             <url-pattern>/zkau/*</url-pattern>
       </servlet-mapping>
[END-CODE]

Another thing! Zk doesn't have support for validation in JSR-303 by default, and for this reason we need to import our validation service to use it here. Do you remember it? Remember validation fragment. We import it by referencing it in Spring OSGi in spring configuration file web-core-osgi-cfg.xml:

XML
[BEGIN-CODE]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
             http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.2.xsd">
 
       <!-- Validator factory -->
       <osgi:reference id="validatorProxy" interface="javax.validation.ValidatorFactory"></osgi:reference>
 
</beans>
[END-CODE]

Also not all we create an abstract bean with "prototype" scope for can be base of all bean controllers. Why prototype? By using prototype, Spring container creates new instances for this object in each call. Why do we need this? Just imagine if we have one instance of this object this means all the web pages access the same object, same reference, and same instance! This causes many problems. To avoid this, Zk stops two instances to the page that uses "singleton" bean as the controller.

"java.lang.IllegalStateException: Access denied: component, <Grid gdListTodo>, belongs to another desktop..."

Okay, our simple abstract bean is in the file web-core-services-cfg.xml:

XML
[BEGIN-CODE]
<?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.xsd">
       
       <!-- Abstract composer bean -->
       <bean id="basicComposer" abstract="true" scope="prototype">
             <property name="validatorFatory" ref="validatorProxy"></property>
       </bean>
       
</beans>[END-CODE]

We finish MVC configuration by creating a base controller for all controllers in this application that have a basic system to use validation JSR-303. For validation we use the "Default" group by default:

C#
[BEGIN-CODE]
public class BasicComposer extends GenericForwardComposer
{
       /**
        *
        */
       private static final long serialVersionUID = 1L;
 
       private ValidatorFactory validatorFatory;
       
       private List<WrongValueException> wrongValues = new ArrayList<WrongValueException>();
 
       /**
        * Validation JSR 303
        *
        * @param <T>: anonymous type
        * @param comp: component
        * @param beanClass: bean class to validate
        * @param propertyName: property name to check and validate
        * @param value: value in question
        */
       protected <T> void validateValue(final Component comp, 
                 final Class<T> beanClass, final String propertyName, 
                 final Object value)
       {
             final Set<ConstraintViolation<T>> violations = 
               this.validatorFatory.getValidator().validateValue(
               beanClass,propertyName, value, Default.class);
             
             // only one violation
             if( violations.size() == 1 )
             {
                    this.wrongValues.add(new WrongValueException(
                       comp, violations.iterator().next().getMessage()));
             }
             // many violations
             else if( violations.size() > 1 )
             {
                    StringBuilder builder = new StringBuilder();
                    for(ConstraintViolation<T> cv : violations)
                    {
                           builder.append(cv.getMessage() + "\n");
                    }
                    builder.deleteCharAt(builder.length() - 1);
                    this.wrongValues.add(new WrongValueException(comp, builder.toString()));
             }
       }
       
       /**
        * Fire validation for throwing exceptions
        *
        * @throws WrongValuesException
        */
       protected void fireValidation() throws WrongValuesException
       {
             if(!this.wrongValues.isEmpty())
             {
                    WrongValueException[] wrongs = 
                      this.wrongValues.toArray(new WrongValueException[]{});
                    this.wrongValues.clear();
                    throw new WrongValuesException(wrongs);
             }
       }
       
       public ValidatorFactory getValidatorFatory()
       {
             return validatorFatory;
       }
 
       public void setValidatorFatory(ValidatorFactory validatorFatory)
       {
             this.validatorFatory = validatorFatory;
       }
}
[END-CODE]

GenericForwardComposer automatically binds components in the ZUL file to Java Code.

Ohhh! We forgot one very big thing; we forgot our template base (like MasterPage in ASP.NET). So we create quickly create a ZUL file in the components folder in WEB-INF like this:

XML
[BEGIN-CODE]
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?page title="Modular J2EE, Level Nightmare" contentType="text/html;charset=UTF-8"?>
<zk>
       <window title="Modular J2EE, Level Nightmare" border="normal">
             <label value="I'm GentlePage =) not MasterPage, Yeah! I'm gentle"></label>
             <hbox self="@{insert(content) }" />
       </window>
</zk>[END-CODE]

The code of Gentle Page stay simple. There are no WOW layouts, just look at the first line and burn it in your mind. This line is used for using spring beans in ZUL files and in other words in ZkScript or Expression Language. Are you excited to test the home page? Okay, we do just use a small URL rewriting after the security setup and we test our home page.

Hacking Setup (Ops Security)

We setup a very basic security by text-plain password in one file “web-hacking-cfg.xml”:

XML
[BEGIN-CODE]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:hacking="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                                        http://www.springframework.org/schema/beans/spring-beans.xsd
                                        http://www.springframework.org/schema/security
                                        http://www.springframework.org/schema/security/spring-security-3.0.xsd">
 
       <hacking:http auto-config="true" use-expressions="true">
             <hacking:intercept-url pattern="/" access="permitAll"/>
             <hacking:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
             <hacking:intercept-url pattern="/jln/**" access="hasRole('ROLE_NO_ONE')"/>
             <hacking:form-login login-page="/login"/>
       </hacking:http>
       
       <hacking:authentication-manager alias="authenticationManager">
             <hacking:authentication-provider>
                    <hacking:user-service>
                           <hacking:user name="admin" password="*" authorities="ROLE_ADMIN"/>
                    </hacking:user-service>
             </hacking:authentication-provider>
       </hacking:authentication-manager>
             
</beans>
[END-CODE]

User is "admin" and the password is "*" (one star, with this password which will never be hacked :D), you can see login.zul page, it is basic and similar to code of demonstration in Zk wiki. Let's jump to rewriting.

URL Rewriting

For what used this URL rewriting? Euh, many things but here we use it to hide real URL path and files extensions, example:

real path of home page is: http://127.0.0.1:8080/jln/index.zul we well change it to http://127.0.0.1:8080/index, I show just a basic example here but this can be used in SEO, etc ..., let is rewrite ...

XML
[BEGIN-CODE]
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN"
        "http://tuckey.org/res/dtds/urlrewrite3.2.dtd">
 
<urlrewrite>
       <rule>
             <from>^/$</from>
             <to type="redirect">/index</to>
       </rule>
       <rule>
             <from>^/([\w-]+)$</from>
             <condition operator="notequal" type="request-url">j_spring_security_check</condition>
             <condition operator="notequal" type="request-url">/zkau.zul</condition>
             <condition operator="notequal" type="request-url">/zkau/*</condition>
             <to type="forward">/jln/$1.zul</to>
       </rule>
             <rule>
             <from>^/admin/([\w-]+)/([\w-]+)$</from>
             <to type="forward">/jln/admin/$1/$2.zul</to>
       </rule>
       <rule>
             <from>^/admin/([\w-]+)/([\w-]+)/(\w+)/(\w+)$</from>
             <to type="forward">/jln/admin/$1/$2.zul?$3=$4</to>
       </rule>
</urlrewrite>
[END-CODE]

Yup, this is all configuration we need for these moments and we can say: the configuration of URL rewriting is now complete.

Just for we can have a little complex demonstration for URL rewriting, we create a folder called jln and we put in it all our web pages. Also our test index page like:

XML
[BEGIN-CODE]
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/WEB-INF/components/gentlepage.zul" ?>
<zk>
       <window self="@{define(content) }" title="Index Page" border="normal">
             <label value="Welcome"></label>
       </window>
</zk>
[END-CODE]

We run our OSGi configuration and we type in browser "http://127.0.0.1:8080" or “http://127.0.0.1:8080/index”, the result is:

image014.gif

Now the big moment comes for you to forge our complete Todo CRUD in a Web Fragment.

Web Fragment

Now I need to repeat how to create a fragment. We will make one for a web core with same structure without configuration files web.xml and urlrewriting.xml, and we create all our ZUL pages in the folder /jln/admin/todo so it can be accessed only by users that have "ROLE_ADMIN." If you forget it scroll up and re-read about this in security configuration.

We craft the ZUL file as a listing of all Todo in database:

XML
[BEGIN-END]
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/WEB-INF/components/gentlepage.zul" ?>
<zk>
       <window self="@{define(content) }" title="List Todo" border="normal" apply="${todoListController }">
             <vbox>
                    <grid id="gdListTodo" >
                           <columns>
                                  <column>ID</column>
                                  <column>Summary</column>
                                  <column></column>
                           </columns>
                    </grid>
                    <div>
                           <button id="btnNew" label="New Todo"></button>
                    </div>
             </vbox>
       </window>
</zk>
[END-CODE]

We meet finally the legendary controller! Finally after all this hard work we code our first real controller:

C#
[BEGIN-CODE]
public class ControllerListTodo extends ControllerBaseTodo
{
 
       /**
        *
        */
       private static final long serialVersionUID = 1L;
 
       private Grid gdListTodo;
       
       @Override
       public void doAfterCompose(Component comp) throws Exception
       {
             super.doAfterCompose(comp);
             this.loadTodoLis();
       }
       
       /**
        * Load todos and add them to grid
        */
       private void loadTodoLis()
       {
             this.gdListTodo.setModel( new ListModelList( this.service.getListTodo() ) );
             this.gdListTodo.setRowRenderer(new TodoRowModel());
       }
 
       /**
        * Redirect to new todo page
        */
       public void onClick$btnNew()
       {
             Executions.sendRedirect("/admin/todo/new");
       }
}
[END-CODE]

We type the URL: http://127.0.0.1:8080/admin/todo/list and get a sweet screen-shot for this ZUL page:

image015.gif

The next screen-shot shows Validation JSR-303 in action:

image016.gif

For others you see them in the source and spring configuration. I think this is the end of our first mission in J2EE Level: Nightmare. We have cheated and kicked all the formal words and definitions. See you later in summary.

Summary

What are you waiting for? I will get fired for leaking these industry secrets! (just kidding xD) The first thing that probably popped in your mind right now is that we are passed all time in configurations

This is true, but the nightmare here is not the configuration but when we set it up, everyhing may become easy.

  • Spring is framework for DI has many modules as Spring Dynamic Module;
  • Spring Dynamic Module is part of this hell JARs called Spring, based in OSGi;
  • OSGi is framework for creating Modular Java Application for Desktop and Web like Zk;
  • Zk is a Web framework for Java use ZUL files, that we hide it by URLrewrite;
  • URLrewrite is an API, used to hide ZUL extensions and rewrite URLs, secured and non-secured by Spring Security;
  • Spring-Security used for securing URLs by “in-memory”, also can use JDBC to get users from database like HSQL;
  • HSQL is “in memory database” we access to it by EclipseLink;
  • EclipseLink is an ORM persistence framework, we cache objects retrieved by EhCache
  • EHCache is an API for caching objects retrieved in memory by EclipseLink via DBCP.
  • DBCP is an API for using pools with Apache Tomcat;
  • And finally, the VIP is Apache tomcat as web server;

I hope you are enjoyed by this first mission, and thank you for reading this article, correcting my errors, critiquing, or suggesting something new or better, etc ... :D.

"Mission Complete"

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Morocco Morocco
Adrabi!, Just another Ghost in the Shell =)

Comments and Discussions

 
GeneralMy Vote of 5 Pin
RaviRanjanKr29-Apr-11 9:27
professionalRaviRanjanKr29-Apr-11 9:27 
GeneralRe: My Vote of 5 Pin
Adrabi Abderrahim29-Apr-11 10:51
Adrabi Abderrahim29-Apr-11 10:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.