Wednesday, July 15, 2009
Web resources filtering with Maven 2
Here is excerpt from jsp file (src/main/webapp/pages/index.jsp):
<script src="<s:url value="/build-${BUILD_NUMBER}/js/yui/yahoo-dom-event/yahoo-dom-event.js"/>" type="text/javascript"></script>
${BUILD_NUMBER} - is just simple placeholder, you can have any property here. (BUILD_NUMBER - it's a Hudson variable, which is very useful as unique identifier, and QA members could always say to you what was wrong and which version of some script wasn't working)
Assume you have standard maven layout for your web project:
So, I need placeholder ${BUILD_NUMBER} replaced by actual value. This is just simple filtering for web application. I want to see something like:
<script src="<s:url value="/build-1231312/js/yui/yahoo-dom-event/yahoo-dom-event.js"/>" type="text/javascript"></script>
But as you know standard filtering, require us to copy some resource from one directory to another. So standard maven filtering is no longer works here, because I don't want to move my jsp files anywhere just for filtering. I found solution in maven war plugin, but solution was only applied to external web resources. I was thinking, can we apply same technique for local resources as for external resources . And the answer is - yes!
Here is example of maven plugin configuration:
<build>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp/pages</directory>
<includes>
<include>**/*.jsp</include>
</includes>
<filtering>true</filtering>
<targetPath>pages</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
...
</build>
So, there is nothing tricky with this configuration: directory is what we want to filter and targetPath just dir, related to build directory. Source and destination directories should have the same structure. If you'll mistype directory name, you would see duplicates of your filtered files, which is not what we wanted.
Conclusion: you could use any file types - css, js, jsp or something else, not binaries of course :) . Web resources filtering helps you when you need some externally configured strings at build time, not at runtime. Such filtering decouples your application from particular environment.
Monday, May 19, 2008
Choosing Erlang IDE
After writing on java for two consecutive years I formed the habit of using my lovely IDE (not a secret it's Intellij IDEA). And I started to dig for something similar in Erlang space.
Though i had 4 candidates :
- Erlide - plugin for eclipse (ver. 0.3.45)
- Erlybird - plugin for netbeans (ver. 0.16.0)
- Ultraedit - win32 text editor + with syntax highlighting
- Emacs - with erlang mode (according to documentation this is a best way of developing erlang apps)
Main criteria in choosing IDE were : syntax highlighting, smart identation, quick compilation of *. erl files, code templates and integrated console.
Maybe because 0 is major release number or some other reasons but the first two IDE Erlide and Erlybird are very raw.
Erlide - is interesting IDE, but many unpredictable errors occured during usage. But in future, i think this IDE has high chances to become ubiquitous. WRT my criterias of choosing IDE it has syntax highlighting, smart identation(very simple) , compilation is made faster after some project configuration(it's not clear). Mark: 3/5.
This IDE has features which I don't evaluate, because the researh was to find most comfortable IDE for code editing,but not most functional.
Erlybird - visually it's fantastic, it has beautiful syntax highlights and identation , autocompletion is good, but some bugs during exploitation is upset me. Mark: 3,5/5.
Ultraedit -well, it's not IDE actually,it's text editor with many features and languages supported, but sadly Erlang natively isn't supported and only using aforementioned wordfile (plugin for ultraedit) we can add support for some pleasant features. Actually it's only syntax highlighting, not great. But this editor has some templates support. Another disadvantage - shareware. Mark: 1,5/5.
Emacs (erlang mode) - well, when I set a goal to research some IDE for Erlang I couldn't predict that emacs will be one of candidates. It was added for comparison because first 3 IDE was not production-ready. Emacs has all you needed in Erlang development. But it has some learning curve in learning emacs finger-crossing:) but you won't be sorry. Mark: 4/5.
So if you want production-ready Erlang use Emacs with Erlang mode. If you developed earlier for Netbeans or Eclipse platform you can choose Erlybird or Erlide but don't forget this IDE are currently in the beginning phase. If you bought Ultraedit and you like them much use Ultraedit with wordfile.
Saturday, April 5, 2008
Struts 2 session replication
Very often the following task occuring: saving and distributing session between web applications. In this post, implemented one of the strategies of saving and distributing session for web framework Struts 2.
Used technologies : EJB3, Struts-2, Tomcat, Jboss
So we have task for linking several web applications with Session Manager. One of the restrictions in this implementation is usage of subdomains of domain e.g. *.domain.com. So we can use domain cookies (this restriction can be taken off with URL rewriting, but in this implementation we use cookies ). Because existed system was build on Struts 2 web framework, we decided to use internal capabilities of this framework. As we will see further, Struts 2 has very exciting futures called Interceptors, and they help us to get rid of crosscutting concernes.
Session Manager is a EJB3 component with simple interface, for simplicity only subset of methods will be shown with descriptive methods signature :
@Remote
public interface SessionManagerRemote {
void extendActivity(String sessionId) throws SessionExpiredException;
String registerActivity();
void unregister(String sessionId);
Long getSessionDuration(String sessionId);
Map getSession (String sessionId) throws SessionExpiredException;
void putSession (String sessionId,MapsessionObjects)throws SessionExpiredException;
}
This component will be deployed on J2EE Application Server, e.g. JBOSS. Jboss implementation following:
@org.jboss.annotation.ejb.Service(objectName = "jboss:managementService=SessionManager")
@Remote(SessionManagerRemote.class)
@org.jboss.annotation.ejb.RemoteBinding(jndiBinding = "com/project/management/session/remote")
public class SessionManager implements SessionManagerRemote {
}
Service annotation is used for Singleton implementation in Jboss runtime.
So, back to main task. We need to replace Struts 2 session implementation with our distributed session, controlled by session manager. But for make such serious session replacement we must understand architecture of Struts 2 framework. First we must detect how we use session in Struts 2 application :
- OGNL expressions with #session identifier e.g. <s:property:value="#session.PARAMETER_IN_SESSION"/>
- Actions that implement SessionAware interface :
public class TempAction implements SessionAware {
private Map session;
public void setSession(Map map) {
this.session = map;
}
}
So we must create our interceptor that
- initialize OGNL value stack with our distributed session
- inject our distributed session in Actions that implements SessionAware interface
According to Struts 2 source code, Dispatcher initialize OGNL value stack, and session in particular:
extraContext.put("session", sessionMap);
and ServletConfigInterceptor injects session in Action classes.So we override such behaviour as following :
//initializing session in OGNL value stack
actionInvocation.getInvocationContext().getContextMap().put("session",distributedSession);
//injecting our session
if (action instanceof SessionAware) {
((SessionAware) action).setSession(distributedSession);
}
So final configuration is following. struts.xml :
<struts>
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="distributedSession" class="com.interceptors.SessionCheckerInterceptor"/>
<interceptor-stack name="distributedSessionStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="distributedSession"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="distributedSessionStack"/>
</package>
<package name="test" extends="default" namespace="/actions">
<action name="logon" class="com.actions.SomeTemporaryAction" method="login">
<result name="success" type="redirectAction">myaccount</result>
<result name="input" type="redirect">/</result>
</action>
</package>
</struts>
We call interceptor distributedSession, and it must be in list after basicStack in order to work consistently.
Final version of our Interceptor :
public class SessionCheckerInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation actionInvocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
String distributedSession = processCookie(request);
String result = null;
Map sessionBeforeInvokation = JNDICacheableFactory.getSessionManager().getSession(distributedSession);
Object action = actionInvocation.getAction();
if (action instanceof SessionAware) {
((SessionAware) action).setSession(sessionBeforeInvokation);
}
actionInvocation.getInvocationContext().setSession(sessionBeforeInvokation);
actionInvocation.getInvocationContext().getContextMap().put("session", sessionBeforeInvokation);
int beforeInvHash = sessionBeforeInvokation.hashCode();
result = actionInvocation.invoke();
Map sessionAfterInvocation = actionInvocation.getInvocationContext().getSession();
int afterInvHash = sessionAfterInvocation.hashCode();
if (afterInvHash != beforeInvHash) {
JNDICacheableFactory.getSessionManager().putSession(distributedSession, sessionAfterInvocation);
}
return result;
}
}
Note: method processCookie omited, in this method cookie processing logic implemented, but if we’d want to realize cross-domain logic we might use URL rewriting for example (in our case we use cookies because we use subdomains of similar domain).
Note: for checking updates in session we compare their hashes, but other implementations are possible. If session changed it is updated.