Monday, May 19, 2008

Choosing Erlang IDE

After having some object-oriented language experience, I decided to move forward in learing programming languages. There are many languages today and each of them correspond to some category. Ubiquity of multicore processors dictate us today new rules for scalable programming systems. And one of the interesting trend in programming - functional programming languages. Maybe I was affected by Steve Vinoski blog or some InfoQ articles , but Erlang language was chosen.

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)
Last variant with emacs occured suddenly during playing with first three candidates. Reason is subconscious resistance to *NIX utils cause win32 legacy.

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,Map sessionObjects)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



  1. initialize OGNL value stack with our distributed session

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