You will be redirected to our new website in 5 seconds...
If you are not automatically taken to our new web site,
please click on the hyperlink :
http://jodd.org
Jodd
build: 309
updated: Jun 17 2008
 

SourceForge.net Logo

freebsd  Support this project

home Home | download | home Contact | SourceForge | File Releases releases | News news |  
Building web applications with WOT
Table of Contents

This tutorial gives step-by-step instructions how to use different features of WOT together in order to build one nice web application.

Introduce Madvoc madvoc

First lets register Madvoc controller in web.xml:

...
	<!-- filters -->
	<filter>
		<filter-name>madvoc</filter-name>
		<filter-class>jodd.madvoc.MadvocDispatcher</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>madvoc</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
		<dispatcher>FORWARD</dispatcher>
	</filter-mapping>
...

Now, lets add some initial index action.

import jodd.madvoc.meta.Action;
import jodd.madvoc.meta.MadvocAction;

@MadvocAction
public class IndexAction {

	@Action
	public void execute() {
		System.out.println("IndexAction.execute");
	}
}
			

Madvoc is already configured to scan classpath for all action classes. After the deploy, action: '/index.html' is handled by IndexAction.execute() and redirected to index.jsp. Simple as that.

Add Petite madvoc petite

We will use Petite as bean container, both for business logic and presentation classes, therefore we have to tell Madvoc we are using Petite-aware web application:

...
	<!-- filters -->
	<filter>
		<filter-name>madvoc</filter-name>
		<filter-class>jodd.madvoc.MadvocDispatcher</filter-class>
		<init-param>
			<param-name>madvoc.webapp</param-name>
			<param-value>jodd.madvoc.PetiteWebApplication</param-value>
		</init-param>
	</filter>
...

And that is all! Lets add some dummy business logic...

import jodd.petite.meta.PetiteBean;

@PetiteBean
public class FooService {

	public String hello() {
		return "hello";
	}
}			

... and wire it in our action class:

@MadvocAction
public class IndexAction {
	
	@PetiteInject
	FooService fooService;
	
	@Action
	public void  execute() {
		System.out.println("IndexAction.execute: " + fooService.hello());
	}
}
			

Almost done. Since we would like to use session scoped beans later, it is required to register following listeners:

...
	<!--listeners-->
	<listener>
		<listener-class>jodd.servlet.RequestContextListener</listener-class>
	</listener>
	<listener>
		<listener-class>jodd.servlet.HttpSessionListenerBroadcaster</listener-class>
	</listener>
...

Attention!
When Madvoc uses Petite container as its action provider, all action classes become aware of Petite context. Therefore, it is possible to inject Petite beans into Madvoc action, although action itself is not a Petite bean (has not @PetiteBean annotation). Madvoc is the one who is responsible for actions life, therefore, an action is instantiated on each request.

Still, it is possible to annotated Madvoc action as Petite bean. In that case, Petite container becomes responsible for action lifecycle, so special attention must be used. For example, if an action is annotated just with @PetiteBean annotation, its default scope is singleton, so there will be just one action instance serving all incoming requests!

Login control with interceptors madvoc

It would be nice if we can trace each request and prevent user to access certain pages until he logins. Scenario might be as follows: if user can't access somne page, login form apears; as soon as he enters his username/password, desired page is opened.

The idea is to have two sets of interceptors: one for all public pages (PublicInterceptorStack, just contains ServletConfigInterceptor), and one for all restricted content (DefaultInterceptorStack), that contains SessionInterceptor, that might looks like:

public class SessionInterceptor implements ActionInterceptor {

	public String intercept(ActionRequest request) throws Exception {
		HttpServletRequest servletRequest = request.getHttpServletRequest();
		HttpSession httpSession = servletRequest.getSession();
		UserSession session = (UserSession) httpSession.getAttribute("user");
		if (session != null) {
			return request.invoke();
		}
		servletRequest.setAttribute("path", request.getActionPath());
		return "chain:" + LOGIN_PAGE;
	}
}
		

As seen, in case user is not logged in, original action request is not invoked, but saved as request attribute, and then, control is passed to the login page. Login page than will have hidden field, and then, the login action might looks like:

@MadvocAction
@InterceptedBy(PublicInterceptorStack.class)
public class LoginAction {

	@In
	String username;
	@In
	String password;
	@In
	String path;

	@In(scope = ScopeType.CONTEXT)
	HttpSession session;

	@Action
	public String post() {
		//User user = userService.findUser(username, password);
		session.setAttribute("user", new UserSession(user));
		return "redirect:" + path;
	}

	@Action
	public void view() {
	}
}

Of course, login action is not complete, but it is shown how to redirect to the desired page.

Transactions with Proxetta

It is a common practice to mark up service methods for transactions. Jodd already provides annotation @Transaction. All we have to do is to put all pieces together: to introduce Proxetta and add transactional aspect. Lets first write the transactional advice:

public class TxAdvice implements ProxyAdvice {

	public Object execute() throws Exception {
		Class type = targetClass();
		String methodName = targetMethodName();

		// read transaction mode from annotation
		JtxTransactionMode txMode = TxAdviceSupport.getTxMode(type, methodName);

		// request transaction
		JtxTransaction tx = null;
		try {
			tx = jtxWorker.maybeRequestTransaction(txMode);
			Object result = invoke();
			jtxWorker.maybeCommitTransaction(tx);
			return result;
		} catch (Exception ex) {
			jtxWorker.markOrRollbackTransaction(tx, ex);
			throw ex;
		}
	}
}		
		

jtxWorker is static reference to an LeanTransactionWorker instance (worked is something that works with transactions in certain way). TxAdviceSupport is external class that just reads transactions mode from annotation and cache that information because of performance. Before we proceed, it is time to step back and do the configuration.

Configuration

Idea is to encapsulate application configuration in one class:

public class App {

	public App() {
		initLogger();
		initProxetta();
		initPetite();
		initDb();
	}

	void initLogger() {
		// init logger
	}

	Proxetta proxetta;

	void initProxetta() {
		ProxyAspect txServiceProxy = new ProxyAspect(TxAdvice.class, new MethodAnnotationPointcut(Transaction.class) {
			@Override
			public boolean apply(MethodSignature msign) {
				return isPublic(msign) && matchClassName(msign, "*Service") && super.apply(msign);
			}
		});
		proxetta = Proxetta.withAspects(txServiceProxy);
	}

	PetiteContainer pc;

	void initPetite() {
		pc = new AppPetiteContainer(proxetta);
		AutomagicPetiteConfig pcfg = new AutomagicPetiteConfig();
		pcfg.configure(pc);
	}

	public static LeanTransactionWorker jtxWorker;

	void initDb() {
		ConnectionProvider connectionProvider;
		// db connection....
		DbJtxTransactionManager jtxManager = new DbJtxTransactionManager(connectionProvider);
		jtxWorker = new LeanTransactionWorker(jtxManager);

		// to be added later...
	}
}
		

Proxetta is configured first - aspect is defined with pointcut on all public service methods that have @Transaction annotation. Then, Petite container is created that is aware of Proxetta:

public class AppPetiteContainer extends PetiteContainer {

	protected final Proxetta proxetta;

	public AppPetiteContainer(Proxetta proxetta) {
		this.proxetta = proxetta;
	}

	@Override
	protected void registerBean(String name, Class type, Class scopeType) {
		type = proxetta.defineProxy(type);
		super.registerBean(name, type, scopeType);
	}
}

This Petite container is then auto-registered by scanning the classpath. Finally, we come to the db and transaction manager configuration.

In order to use custom Petite container, we have to create custom web application, that will also include web-based configuration such as default interceptors definition:

public class AppWebApplication extends PetiteWebApplication {

	public static final String LOGIN_PAGE = "/login.html";

	@Override
	@SuppressWarnings({"unchecked"})
	protected void initWebConfiguration() {
		super.initWebConfiguration();
		defaultInterceptors = new Class[] {DefaultInterceptorStack.class};
	}

	App app;

	@Override
	protected void initPetiteContainer() {
		app = new App();
		pc = app.pc;
	}
		

Of course, minor change is required in the web.xml to replace previous web application with the new one.

Database access

to be written...