<file_sep>package net.ion.nchat.aradon;

import java.util.Timer;
import java.util.TimerTask;

import net.ion.radon.core.Aradon;
import net.ion.radon.core.let.AbstractServerResource;

import org.restlet.resource.Delete;
import org.restlet.resource.Get;

public class ShutdownFrontLet extends AbstractServerResource {

	
	@Get
	public String getMyName(){
		return "Hello " + getInnerRequest().getFormParameter().get("name");
	}
	
	@Delete
	public String suicide(){
		long timeoutMili = Math.max(getInnerRequest().getParameterAsInteger("timeout"), 1) * 1000L ;
		
		final Aradon self = getAradon() ;
		new Timer().schedule(new TimerTask(){
			
			@Override public void run() {
				self.stop() ;
				System.exit(0) ;
			}
			
		}, timeoutMili) ;
		
		return timeoutMili + "(ms) shutdown.." ;
	}
}
<file_sep>package net.ion.nchat.aradon;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import net.ion.framework.util.StringUtil;
import net.ion.radon.core.filter.IRadonFilter;
import net.ion.radon.core.security.ChallengeAuthenticator;

import org.restlet.Request;
import org.restlet.Response;
import org.restlet.security.MapVerifier;
import org.restlet.security.Verifier;

public class AdminPageFilter extends ChallengeAuthenticator {

	private AdminPageFilter(String startwithIp) {
		super("admin zone", new MyVerifier(startwithIp, "kalce", "bleujin"));
	}

	public AdminPageFilter(String startwithIp, String id, String pwd) {
		super("admin zone", new MyVerifier(startwithIp, id, pwd));
	}
	
	public static IRadonFilter create() {
		return new AdminPageFilter("61.250.201.");
	}

	public static IRadonFilter test(String startwithIp) {
		return new AdminPageFilter(startwithIp);
	}

}

class MyVerifier implements Verifier {

	private String startWithIp;
	private Verifier admin;

	public MyVerifier(String startwithIp, String adminId, String adminPwd) {
		this.startWithIp = startwithIp;
		ConcurrentMap<String, char[]> users = new ConcurrentHashMap<String, char[]>();
		users.put(adminId, adminPwd.toCharArray());
		this.admin = new MapVerifier(users);
	}

	public int verify(Request request, Response response) {

		String clientIp = request.getClientInfo().getAddress();
		if (StringUtil.isBlank(clientIp) || clientIp.startsWith(startWithIp)) {
			return 4;
		} else {
			return admin.verify(request, response);
		}
	}

}
<file_sep>Aradon sample 
============

aradon websocket sample

1. download
2. cd ./alone
3. set value at java_home in aradon.bat
4. execute aradon.bat

5. browse 
	http://ipaddress:9000/rest/client/1234/jin
	
 	http://ipaddress:9000/rest/client/1234/hero
 	
 	http://ipaddress:9000/rest/event/1234 <file_sep>package net.ion.nchat.context;

import java.io.Serializable;

import net.ion.craken.AbstractEntry;
import net.ion.craken.EntryKey;
import net.ion.craken.simple.EmanonKey;

public class SerializedChatMessage extends AbstractEntry<SerializedChatMessage> implements Serializable {

	private static final long serialVersionUID = 6153003775018649621L;
	private EntryKey key ;
	private NormalMessagePacket msgPacket ;
	private SerializedChatMessage(String msgid) {
		this.key = EmanonKey.create(msgid) ;
	} ;
	
	@Override
	public EntryKey key() {
		return key;
	}

	public SerializedChatMessage chat(NormalMessagePacket msgPacket) {
		this.msgPacket = msgPacket.toRoot() ;
		return this ;
	}

	public String sender() {
		if (msgPacket == null) return "" ;
		return msgPacket.getString("head.sender");
	}

	public String topic() {
		return msgPacket.getString("head.topicId");
	}

	public String getFullString() {
		return msgPacket.getFullString();
	}

}
<file_sep>package net.ion.nchat.aradon;

import java.util.List;
import java.util.Map;

import net.ion.framework.util.ListUtil;
import net.ion.framework.util.MapUtil;
import net.ion.nchat.async.SampleChatHandler;
import net.ion.nradon.WebSocketConnection;
import net.ion.radon.core.let.DefaultLet;
import net.ion.radon.core.let.WSPathService;

import org.restlet.representation.Representation;

public class SampleMonitorLet extends DefaultLet {

	@Override
	protected Representation myGet() throws Exception {

		WSPathService wspath =  getSectionService().getAradon().getChildService("async").wspath("chat") ;
		SampleChatHandler handler = (SampleChatHandler)wspath.websocketResource();
		
		List<WebSocketConnection> conns = handler.getAllConnectors();
		
		List<Map<String, ?>> data = ListUtil.newList() ;
		for (WebSocketConnection conn : conns) {
			Map<String, Object> cinfo = MapUtil.newMap() ;
			cinfo.put(WSPathService.VAR_SESSIONID, conn.data(WSPathService.VAR_SESSIONID)) ;
			cinfo.put("datas", conn.httpRequest().data() ) ;
			cinfo.put("remoteAddress", conn.httpRequest().remoteAddress()) ;
			cinfo.put("timestamp", conn.httpRequest().timestamp()) ;
			cinfo.put("headers", conn.httpRequest().allHeaders()) ;
			cinfo.put("cookies", conn.httpRequest().cookies()) ;
			data.add(cinfo) ;
		}
		
		return toRepresentation(data); 
	}

}
<file_sep>package net.ion.nchat.async;

import java.util.Collections;
import java.util.List;

import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;

import net.ion.craken.Craken;
import net.ion.craken.EntryKey;
import net.ion.craken.LegContainer;
import net.ion.framework.util.Debug;
import net.ion.framework.util.ListUtil;
import net.ion.framework.util.ObjectId;
import net.ion.nchat.context.ChatCraken;
import net.ion.nchat.context.IMessagePacket;
import net.ion.nchat.context.NormalMessagePacket;
import net.ion.nchat.context.SerializedChatMessage;
import net.ion.nradon.AbstractWebSocketResource;
import net.ion.nradon.Radon;
import net.ion.nradon.WebSocketConnection;
import net.ion.nradon.handler.event.ServerEventHandler;
import net.ion.nradon.handler.event.ServerEvent.EventType;

public class SampleChatHandler extends AbstractWebSocketResource implements ServerEventHandler {

	private List<WebSocketConnection> conns = ListUtil.newList();
	private LegContainer<SerializedChatMessage> container;

	public void onClose(WebSocketConnection conn) throws Exception {
		conns.remove(conn);
		NormalMessagePacket message = NormalMessagePacket.create().inner("body").put("message", conn.data("userId") + " logout");
		chat(conn, message);
	}

	public void onMessage(WebSocketConnection conn, String msg) throws Throwable {
		NormalMessagePacket message = NormalMessagePacket.load(msg) ;
		chat(conn, message);

	}

	public void onMessage(WebSocketConnection conn, byte[] arg1) throws Throwable {

	}

	public void onOpen(WebSocketConnection conn) throws Exception {
		conns.add(conn);
		NormalMessagePacket message = NormalMessagePacket.create().inner("body").put("message", conn.data("userId") + " login");
		chat(conn, message);
	}

	public void onPong(WebSocketConnection conn, String msg) throws Throwable {

	}

	private void chat(WebSocketConnection sender, NormalMessagePacket packet) {
		String msgId = new ObjectId().toString();
		container.newInstance(msgId).chat(packet.toRoot().inner("head").put("sender", sender.data("userId")).put("topicId", sender.data("topicId")) ).save();

	}
	
	void broadCastToRoom(SerializedChatMessage msg) {
		for (WebSocketConnection conn : conns) {
			if (conn.data("userId").equals(msg.sender())) continue;
			if (conn.data("topicId").equals(msg.topic())) {
				conn.send(msg.getFullString());
			}
		}
	}

	public void onEvent(EventType eventtype, Radon radon) {
		if (eventtype == EventType.START) {
			Craken craken = radon.getConfig().aradon().getServiceContext().getAttributeObject(ChatCraken.class.getCanonicalName(), ChatCraken.class).getCraken();
			this.container = craken.defineLeg(SerializedChatMessage.class);
			this.container.addListener(new ChatListener(this));
		}
	}

	@Listener
	public class ChatListener {

		private SampleChatHandler handler;

		public ChatListener(SampleChatHandler handler) {
			this.handler = handler;
		}

		@CacheEntryModified
		public void cacheModified(CacheEntryModifiedEvent<EntryKey, SerializedChatMessage> e) {
			if (e.isPre())
				return;

			handler.broadCastToRoom(e.getValue());
		}

	}

	public List<WebSocketConnection> getAllConnectors() {
		return Collections.unmodifiableList(conns) ;
	}

}
<file_sep>package net.ion.nchat.util;

import java.io.File;
import java.io.IOException;

import net.ion.radon.core.IService;
import net.ion.radon.core.config.PlugInsConfiguration;
import net.ion.radon.core.context.OnOrderEventObject;

public class FileFinder implements OnOrderEventObject {

	private String pluginId;

	public FileFinder(String pluginId) {
		this.pluginId = pluginId;
	}

	public File findFile(IService service, String relativePath) {
		try {
			PlugInsConfiguration pluginConfig = service.getAradon().getGlobalConfig().plugin();
			File foundFile = null ;
			try {
				foundFile = pluginConfig.findPlugInFile(pluginId, "/" + relativePath);
			} catch(IllegalArgumentException notfound){
				foundFile = new File(relativePath);
			}
			if (!foundFile.exists())
				throw new IllegalStateException(foundFile.getCanonicalPath() + " not exists");
			return foundFile;
		} catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

	@Override
	public int order() {
		return -1;
	}

	@Override
	public void onEvent(AradonEvent event, IService service) {
		// TODO Auto-generated method stub

	}
}
<file_sep><?xml version="1.0"?>
<project name="Jar Publish" default="publish" basedir=".">
	
	<property name="baseDir" value="." />
	<property name="binDir" value="./bin" />
	<property name="publishDir" value="./publish" />
	<property name="libDir" value="./libs/" />
	<tstamp>
	  <format property="TODAY_MY" pattern="MM/dd/yyyy hh:mm"/>
	</tstamp>
	
	<property name="version.number" value="0"/>
	<property name="build.number" value="3"/>
	

    <target name="apache_fat">
    	<delete file="${libDir}/apache_fat.jar"></delete>
        <fatjar.build output="${libDir}/apache_fat.jar">
            <fatjar.manifest/>
        	<fatjar.jarsource file="${libDir}\apache\commons-beanutils-1.8.0.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\commons-codec-1.4.jar" relpath=""/>  
        	<fatjar.jarsource file="${libDir}\apache\commons-fileupload-1.2.1.jar" relpath=""/>
        	<fatjar.jarsource file="${libDir}\apache\ecs-1.4.2.jar" relpath=""/>
        	
        	<fatjar.jarsource file="${libDir}\apache\commons-collections-3.2.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\commons-configuration-1.6.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\commons-io-1.4.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\commons-lang-2.4.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\commons-logging-1.1.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\javolution-5.5.1.jar" relpath=""/>  
        	<fatjar.jarsource file="${libDir}\apache\json-2-RELEASE65.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\log4j-1.2.16.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\netsf-json-lib-2.2.2-jdk15.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\servlet-api-2.5-6.1.14.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\slf4j-api-1.5.6.jar" relpath=""/>  
        	<fatjar.jarsource file="${libDir}\apache\slf4j-jdk14-1.5.10.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\apache\slf4j-log4j12-1.5.8.jar" relpath=""/> 
        </fatjar.build>
    </target>
    
	 <target name="aradon_fat">
    	<delete file="${libDir}/aradon_fat_0.5.jar"></delete>
        <fatjar.build output="${libDir}/aradon_fat_0.5.jar">
            <fatjar.manifest/>
        	<fatjar.jarsource file="${libDir}\aradon\aradon_0.5.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\aradon\iframework_2.2.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\aradon\jetty_fat.jar" relpath=""/> 
        	<fatjar.jarsource file="${libDir}\aradon\rest_fat.jar" relpath=""/> 
        </fatjar.build>
    </target>

	
	<target name="test_websocket" depends="apache_fat, aradon_fat" >
		<property name="test.reports" value="./resource/report" /> 

		<path id="test.classpath">
			<pathelement location="bin" />
			<pathelement path="libs/apache/junit.jar" />
			<fileset dir="libs">
			    <include name="*.jar"/>
		  </fileset>
		</path>

		<junit printsummary="on" haltonfailure="on" fork="true">
			<classpath refid="test.classpath" />
			<formatter type="xml" />
			<test name="net.ion.websocket.TestAllWebSocket" todir="resource"/>
		</junit>
		<!--
		<junit fork="yes" printsummary="no" haltonfailure="no">
		    <batchtest fork="yes" todir="${test.reports}" >
		      <fileset dir="${classes}">
		        <include name="**/*Test.class" />
		      </fileset>
		    </batchtest>
		    <formatter type="xml" />
		    <classpath refid="test.classpath" />
		  </junit>

		<junitreport todir="${test.reports}">
		    <fileset dir="${test.reports}">
		      <include name="TEST-*.xml" />
		    </fileset>
		    <report todir="${test.reports}" />
		</junitreport> -->
	</target>

	
	<target name="publish" depends="test_websocket">
		<property name="manifest.main.class" value="net.ion.websocket.server.ServerNodeRunner" />
		<property name="manifest.classpath" value="lib/apache_fat.jar lib/aradon_fat_0.5.jar lib/netty-3.2.5.Final.jar"/> 
		<!-- delete  file="${libDir}/websocket_${version.number}.${build.number}.jar" /-->
		<jar destfile="publish/websocket_${version.number}.${build.number}.jar">
			<manifest>
				<attribute name="Manifest-Version" value="1.0" />
				<attribute name="Built-By" value="${user.name}" />
				<attribute name="Created-By" value="${user.name}" />
				<attribute name="Main-Class" value="${manifest.main.class}" />
				<attribute name="Built-Date" value="${TODAY_MY}" />
				<attribute name="Class-Path" value="${manifest.classpath}" />
				<section name="common">
					<attribute name="Specification-Title" value="websocket" />
					<attribute name="Specification-Version" value="${version.number}.${build.number}" />
					<attribute name="Specification-Vendor" value="i-on" />
					<attribute name="Specification-Dev" value="bleujin,minato" />
				</section>
			</manifest>
			<fileset dir="${binDir}/" includes="net/**" />
		</jar>
	</target>
	


</project><file_sep>package net.ion.nchat.context;

import java.io.File;
import java.io.IOException;

import net.ion.craken.Craken;
import net.ion.nchat.util.FileFinder;
import net.ion.radon.core.IService;
import net.ion.radon.core.context.OnOrderEventObject;

import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.eviction.EvictionStrategy;

public class ChatCraken implements OnOrderEventObject {

	private final String clusterName;
	private final String configPath;
	private Craken craken;

	public ChatCraken(String clusterName, String configPath) {
		this.clusterName = clusterName;
		this.configPath = configPath;
	}

	public final static ChatCraken test() {
		return new ChatCraken("my-cluster", "resource/config/jgroups-udp.xml");
	}

	@Override
	public void onEvent(AradonEvent ae, IService service) {
		if (ae == AradonEvent.START) {
			try {
				this.craken = Craken.create();
				FileFinder ff = service.getServiceContext().getAttributeObject(FileFinder.class.getCanonicalName(), FileFinder.class) ;
				File configFile = ff.findFile(service, configPath);
				
				craken.globalConfig().transport().clusterName(clusterName).addProperty("configurationFile", configFile.getCanonicalPath());
				craken.preDefineConfig(SerializedChatMessage.class, new ConfigurationBuilder().clustering().cacheMode(CacheMode.REPL_ASYNC).clustering().eviction().maxEntries(10000).strategy(EvictionStrategy.LIRS).build());
				craken.start();
			} catch (IOException ex) {
				throw new IllegalStateException(ex);
			}
		} else if (ae == AradonEvent.STOP) {
		}
	}

	public Craken getCraken() {
		if (craken == null)
			throw new IllegalStateException("not initialized craken");
		return craken;
	}

	@Override
	public int order() {
		return 0; // first
	}
}
