What i have is a simple Stateless Session EJB (Enterprise Java Bean). The J2EE container model uses a callback invocation + client-server architecture so that the container can run both client and lifecycle functions.
So, how do you accomplished the above to the Erlang equivalent? The toughest part is building a framework that will invoke the lifecycle methods like those ejbXXX as well as conformation to the specifications laid out by the J2EE Community and i will skip that part, for now and hence, i will concentrate on the client-server part as well as hot code swapping. Below is my attempt at the above mentioned J2EE implementation.
Filename: "Converter.java" --- Actual EJB remote class
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.math.*;
public interface Converter extends EJBObject {
public BigDecimal dollarToYen(BigDecimal dollars) throws RemoteException;
public BigDecimal yenToEuro(BigDecimal yen) throws RemoteException;
}
Filename: "ConverterHome.java" --- Actual EJB Home class
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface ConverterHome extends EJBHome {
Converter create() throws RemoteException, CreateException;
}
Filename: "ConverterBean.java" --- Actual EJB class
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import java.math.*;
public class ConverterBean implements SessionBean {
BigDecimal yenRate = new BigDecimal("121.6000");
BigDecimal euroRate = new BigDecimal("0.0077");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public ConverterBean() {}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
Filename: "callback.erl"
-module(callback).
-export([init/0, ejbActivate/0, ejbPassivate/0, ejbRemove/0, dollarToYen/1, yenToEuro/1, handle/2]).
-import(container, [rpc/2]).
%% client routines
dollarToYen(Dollars) -> rpc(ejbserver, {convertToYen, Dollars}).
yenToEuro(Yen) -> rpc(ejbserver, {convertToEuro, Yen}).
ejbActivate() -> rpc(ejbserver, {ejbactivate}).
ejbPassivate() -> rpc(ejbserver, {ejbpassivate}).
ejbRemove() -> rpc(ejbserver, {ejbremove}).
%% callback routines
init() -> dict:new().
handle({convertToYen, Dollars}, Dict) -> { Dollars * 126, Dict};
handle({ejbactivate}, Dict) -> { ejbActivate , Dict};
handle({ejbpassivate}, Dict) -> { ejbPassivate , Dict};
handle({ejbremove}, Dict) -> { ejbRemove , Dict};
handle({convertToEuro, Yen}, Dict) -> {Yen * 0.0077, Dict}.
Filename: "container.erl"
-module(container).
-export([start/2, rpc/2, swap_code/2]).
start(Name, Mod) ->
register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)).
swap_code(Name,Mod) -> rpc(Name, {swap_code, Mod}).
%
% Standard code for abstracting the "RPC-call" layer
%
rpc(Name, Request) ->
Name ! {self(), Request},
receive
{Name, Response} -> Response
end.
%
% Standard code for looping and waiting for messages from clients
%
loop(Name, Mod, OldState) ->
receive
{From, {swap_code, NewCallbackMod}} ->
From ! {Name, ack},
loop(Name, NewCallbackMod, OldState);
{From, Request} ->
{Response, NewState} = Mod:handle(Request,OldState),
From ! {Name, Response},
loop(Name, Mod, NewState)
end.
So how do you run it ? Refer to the figure below
I shall briefly explain what i did,
1/ compile the "server"
2/ compile the "callback" a.k.a. EJB
3/ start up the "server"
4/ invoke the callback / EJB to run the interface functions
So, the next thing is how do i do hot code swapping without bringing down the server? What you need to do first is to decide what your replacement code is going to be and swap it with the code that was just running. E.g. let's assume that i have decided to replace the callback.erl with another code callback2.erl and i want to replace the former with the latter.
The changes are highlighted in bold for easy viewing. After, you need to compile it and swap it with the running code. Note that the Mod:init() does not run again. Refer to the figure below for a sample hot code swapping.
Filename: "callback2.erl"
-module(callback2).
-export([init/0, ejbActivate/0, ejbPassivate/0, ejbRemove/0, dollarToYenSpecial/1, yenToEuroSpecial/1, handle/2]).
-import(container, [rpc/2]).
%% client routines
dollarToYenSpecial(Dollars) -> rpc(ejbserver, {convertToYen, Dollars}).
yenToEuroSpecial(Yen) -> rpc(ejbserver, {convertToEuro, Yen}).
ejbActivate() -> rpc(ejbserver, {ejbactivate}).
ejbPassivate() -> rpc(ejbserver, {ejbpassivate}).
ejbRemove() -> rpc(ejbserver, {ejbremove}).
%% callback routines
init() -> dict:new().
handle({convertToYen, Dollars}, Dict) -> { Dollars * 126 * 126, Dict};
handle({ejbactivate}, Dict) -> { ejbActivate , Dict};
handle({ejbpassivate}, Dict) -> { ejbPassivate , Dict};
handle({ejbremove}, Dict) -> { ejbRemove , Dict};
handle({convertToEuro, Yen}, Dict) -> {Yen * 0.0077 * 0.0077, Dict}.
I hope this demonstrates how powerful Erlang can be and possibly a replacement language for Java and the like?
1 comment:
Good for people to know.
Post a Comment