jZeno

pure java web development

  • Increase font size
  • Default font size
  • Decrease font size

Business Layer - Business Facades

E-mail Print PDF

This layer contains the business logic to get and store data in the data layer. Business facades and GUI components can be reloaded by jZeno if you put them in the src/rjava folder, meaning you don't need to restart your server when code has changed but you can just click the "reload" button. Now for the business facades. The following business facades are needed for the tutorial to be able to work :

  • net.sf.jzeno.tutorial.business.BusinessFactory.java
  • net.sf.jzeno.tutorial.business.OrderCriteria.java
  • net.sf.jzeno.tutorial.business.OrderFacade.java
  • net.sf.jzeno.tutorial.business.OrderFacadeImpl.java
  • net.sf.jzeno.tutorial.business.ProductCriteria.java
  • net.sf.jzeno.tutorial.business.ProductFacade.java
  • net.sf.jzeno.tutorial.business.ProductFacadeImpl.java
  • net.sf.jzeno.tutorial.business.TestDbSetupFacade.java (This facade fills the database with a customer and admin user)
  • net.sf.jzeno.tutorial.business.TestDbSetupFacadeImpl.java
  • net.sf.jzeno.tutorial.business.UserManagerFacade.java
  • net.sf.jzeno.tutorial.business.UserManagerFacadeImpl.java

The contract for writing business facades under jzeno is that you create an interface with the facade name you want to use and add the suffix “Impl” for the implementation. jZeno will then look for the facade in the package "business.package", which you specify in the file configuration.properties.

configuration.properties 

This file specifies jZeno specific configuration properties. 

#
#Fri Jul 27 15:56:25 CEST 2007
use.precreation=true
source.folder=/home/codehawk/jZeno/template/src/java
suppress.security.aspect=false
global.exception.handler.class.name=net.sf.jzeno.DefaultGlobalExceptionHandler
default.layout.classname=net.sf.jzeno.echo.ZenoLayout
enable.class.reloading=true
business.package=net.sf.jzeno.tutorial.business
style.class.name=net.sf.jzeno.echo.GenericStyle
mail.sendmail=false
invalid.marker.class.name=net.sf.jzeno.echo.SpecificInvalidMarker
override.readonly.attributes=false
application.title=jZeno Tutorial
initial.screen.classname=net.sf.jzeno.tutorial.screen.LoginScreen
optimistic.lock.exception.handler.class.name=net.sf.jzeno.DefaultOptimisticLockExceptionHandler
timeformat.class.name=net.sf.jzeno.util.ZenoTimeFormat
dateformat.class.name=net.sf.jzeno.util.ZenoDateFormat

Note the entry business.package sets up the package containing the business facades. (In fact multiple packages can be specified here comma-seperated).

BusinessFactory

If you want to use a facade you first need to request it from jZeno by calling BusinessFactory.getFacade("InterfaceName").

For convenience it is possible to create a type-safe subclass of AbstractBusinessFactory that contains convenience methods for obtaining your business facades.

package net.sf.jzeno.tutorial.business;

import net.sf.jzeno.aop.AbstractBusinessFactory;

public class BusinessFactory extends AbstractBusinessFactory {
  public static ProductFacade getProductFacade() {
    return (ProductFacade) getFacade("ProductFacade");
  }

  public static UserManagerFacade getUserManagerFacade() {
    return (UserManagerFacade) getFacade("UserManagerFacade");
  }

  public static TestDbSetupFacade getTestDbSetupFacade() {
    return (TestDbSetupFacade) getFacade("TestDbSetupFacade");
  }

  public static OrderFacade getOrderFacade() {
    return (OrderFacade) getFacade("OrderFacade");
  }
}

UserManagerFacade

Our UserManagerFacade implements jZeno's UserManagerFacade because jZeno needs to now how to create an anonymous user. Such an anonymous user is used to establish a security context for users that have not logged on to the system. Typically this would be an implementation of net.sf.jzeno.model.User with very little permissions in the system. The class implementing this createAnonymousUser must be called UserManagerFacade (for the interface and UserMangerFacadeImpl for the implementation). 

The authenticate() method will be called by our LoginScreen. Notice the use of the Hibernate criteria API, at first it may seem a bit awkward, but when queries start becoming more complex the Criteria API stays much more manageable. (Unfortunatly not everything that can be done in HQL is possible in the criteria API... - So it's not allways possible to use the Criteria API).

PersistencySupport.swizzle(user, "roles.permissions"); tells jZeno to also load the attached roles and their permissions. You need to do this because once you leave the business method your hibernate session will be closed and you won't be ably to lazy load the user's relates model objects. In this case we know that we will also need the user's roles and their permissions.

Now look at the save() method. Notice that we return the object we save. This is important because when a client passes an object to a facade, jZeno perfomrs calls to the business layers with call-by-value semantics. So the object in the business method is different instance from the one on the client, any change made to it will not be visible to the client. Most of the time you won't even notice there is a problem since it is likely you want to update the database to values entered on the screen so you can't really see the object you are using actually represents old data. However, on the server there is also the version field (remember our AbstractTutorialEntity class) which gets incremented on each update, and this field also needs to be kept in sync on the client. If the client doesn't refresh the object after a database change, the version field in the database would become greater than on the presentation layer. If you then want to update this object a second time, hibernate would see that the version of the object you want to update is smaller than the one in the database, and a OptimisticLockException will be thrown.

Interface 

package net.sf.jzeno.tutorial.business;

import net.sf.jzeno.tutorial.model.User;

public interface UserManagerFacade extends
    net.sf.jzeno.business.UserManagerFacade {

  User authenticate(String username, String password);

  User save(User user);
}

Implementation 

package net.sf.jzeno.tutorial.business;

import net.sf.jzeno.aop.Criteria;
import net.sf.jzeno.aop.PersistencySupport;
import net.sf.jzeno.tutorial.model.User;

import org.hibernate.criterion.Restrictions;

public class UserManagerFacadeImpl implements UserManagerFacade {

  public User authenticate(String userName, String password) {
    Criteria crit = PersistencySupport.createCriteria(User.class);
    crit.add(Restrictions.eq("userName", userName));
    crit.add(Restrictions.eq("password", password));
    User user = (User) crit.uniqueResult();

    PersistencySupport.swizzle(user, "roles,roles.permissions");
    return user;
  }

  public User save(User user) {
    return user;
  }

  public User createAnonymousUser() {
    return new User();
  }
}

ProductFacade

The find() method illustrates a way to retrieve objects based upon variable parameters. Create a "Criteria" object which contains a nullable property for each field you want to filter at the moment we only need to find a product by name so there's no need in adding additional criteria, but if the need should arise to find products by description or price, you can add them).

ProductCriteria

package net.sf.jzeno.tutorial.business;

public class ProductCriteria {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

ProductFacadeImpl

 

package net.sf.jzeno.tutorial.business;

import java.util.List;

import net.sf.jzeno.aop.Criteria;
import net.sf.jzeno.aop.PersistencySupport;
import net.sf.jzeno.tutorial.model.Product;

import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

public class ProductFacadeImpl implements ProductFacade {
  public Product save(Product p) {
    return p;
  }

  @SuppressWarnings("unchecked")
  public List find(ProductCriteria productCriteria) {
    Criteria crit = PersistencySupport.createCriteria(Product.class);

    if (productCriteria != null) {
      if (productCriteria.getName() != null) {
        crit.add(Restrictions.eq("name", productCriteria.getName()));
      }
    }

    crit.addOrder(Order.asc("name"));
    return crit.list();
  }
}