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
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
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:
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
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:
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.