Getting started with Makai/JAMP
From Resin 4.0 Wiki
Brief introduction to Makai and JAMP
Makai (TM) is not the final name for this technology. Makai (TM) is a services model based on concepts of high speed, mechanical sympathy computing. Underneath the covers, Makai (TM) is a super charged race engine for maximizing throughput. It is an inherently non-blocking, multithreaded model. It is similar in concept to the LMAX disruptor, apartment thread model, actor model and active objects model. It is none of these, and it is all of these.
From a developer standpoint Makai (TM) allows you to write services that look like regular Java services. It takes care of thread management, marshaling (JSON or Hessian), and many more things that we will introduce later. Makai (TM) is super charged SOA. With Makai (TM), you can even turn on journaling and use its high speed, reliable async storage to manage all of your operational data with speeds up to 1,000,000 operations per second on commodity server hardware and several million operations per second with souped up servers.
Makai (TM) services that you write can be accessible within a JVM, or via REST or Websockets.
Example Java App to manage a TODO list
package example; import io.makai.core.Export; import io.makai.core.Service; import java.util.HashSet; import java.util.Set; @Service("/taskService") @Export public class TaskService { TaskManager manager = new TaskManager(); OwnerRepo repo = new OwnerRepo(); Group group = new Group(repo, "shared"); public String addTask(String taskName) { Task task = new Task(taskName); manager.addTask(group, task); return task.id(); } public boolean changeDescription(String id, String description) { Task task = manager.getTaskBy(id); task.setDescription(description); return true; } public Set<TaskData> tasks() { Set<TaskData> taskData = new HashSet<>(); for (Task task : manager.tasks()) { taskData.add(new TaskData(task)) ; } return taskData; } }
Files for this project:
. ├── makaiExample.iml ├── pom.xml ├── taskListModel │ ├── model.iml │ ├── pom.xml │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── example │ │ │ │ ├── Group.java │ │ │ │ ├── OwnerRepo.java │ │ │ │ ├── Person.java │ │ │ │ ├── Task.java │ │ │ │ ├── TaskManager.java │ │ │ │ ├── TaskOwner.java │ │ │ │ └── Utils.java │ │ │ └── resources │ │ └── test │ │ └── java │ │ └── io │ │ └── jamp │ │ └── example │ │ └── model │ │ └── TaskManagerTest.java │ └── taskListModel.iml └── taskListService ├── pom.xml ├── src │ └── main │ ├── java │ │ ├── example │ │ │ ├── TaskData.java │ │ │ └── TaskService.java │ │ └── io │ │ └── makai │ │ └── example │ ├── resources │ │ └── META-INF │ │ └── beans.xml │ └── webapp │ ├── META-INF │ │ └── MANIFEST.MF │ ├── WEB-INF │ │ └── resin-web.xml │ └── todo.html └── taskListService.iml
Support classes, Task.java and TaskManager.java
package example; import java.util.Random; public class Task { private static final String NULL = "NULL"; private static int count =0; private long timestamp; private String objectId=NULL; private String name=NULL; private String description=NULL; private TaskOwner owner = TaskOwner.NO_OWNER; public Task task(String name) { Task task = new Task(name); return task; } public Task(String name) { this.timestamp = System.currentTimeMillis(); this.name = name; this.objectId = String.format("%s-%s-%s-%s", name, new Random().nextLong(), System.nanoTime(), count++); } public TaskOwner owner() {return owner;} public String id() {return objectId;} public void setOwner(TaskOwner owner) { this.owner = owner; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public boolean equals(Object o) { Task task = (Task) o; if (objectId != null ? !objectId.equals(task.objectId) : task.objectId != null) return false; return true; } @Override public int hashCode() { return objectId.hashCode(); } @Override public String toString() { return "Task{" + "timestamp=" + timestamp + ", objectId='" + objectId + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + ", owner=" + owner + '}'; } public long getTimestamp() { return timestamp; } }
package example; import java.util.*; public class TaskManager { private Set<Task> tasks = new HashSet<Task>(); private transient Map<TaskOwner, Set<Task>> ownerToTaskMap = new HashMap<>(); private transient Map<String, Task> idToTask = new HashMap<>(); public void addTask(TaskOwner owner, Task task) { task.setOwner(owner); tasks.add(task); Set<Task> ownerTasks = ownerToTaskMap.get(owner); if (ownerTasks == null) { ownerTasks = new HashSet<>(); } ownerTasks.add(task); idToTask.put(task.id(), task); } public void removeTask(Task task) { tasks.remove(task); idToTask.remove(task.id()); Set<Task> ownerTasks = ownerToTaskMap.get(task.owner()); if (ownerTasks == null) { ownerTasks = new HashSet<>(); } ownerTasks.remove(task); } public Set<Task> tasks() { return tasks; } public Set<Task> listTasksForOwner(TaskOwner doer) { return ownerToTaskMap.get(doer); } public Task getTaskBy(String id) { return idToTask.get(id); } }
<html> <head> <title>Todo List Client</title> <link rel="stylesheet" type="text/css" href="css/style.css" media="screen" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script> <script type="text/javascript"> var ENTER_KEY = '13'; var messageId = 1; var calls = new Array(); var todoService = { version : "1.0", webSocketURL : buildWebSocketURL(), webSocket : null,//WebSocket userName : null }; function invokeJampMethod(serviceName, methodName, callback, args) { var list = new Array(); list.push("query"); list.push({}); list.push("me"); list.push(messageId); list.push(serviceName); list.push(methodName); for (var index=0; index < args.length; index++) { var obj = args[index]; list.push(obj); } if (todoService.webSocket.readyState == WebSocket.OPEN) { todoService.webSocket.send(JSON.stringify(list)); todoService.status(JSON.stringify(list)); $("#tasks").prepend("<p style='color:black'>" + JSON.stringify(list) + "</p>"); var call = { queryId: messageId, "serviceName": serviceName, "methodName" : methodName, func: callback, "args": args}; calls[messageId]=call; } else { todoService.status("websocket unable to send "); } messageId++; } function buildWebSocketURL() { var url = document.URL; var parts = url.split('/'); var scheme = parts[0]; var hostPort = parts[2]; var wssScheme = null; if (scheme=="http:") { wssScheme="ws:"; } else if (scheme=="https:") { wssScheme="wss:"; } wssUrl = wssScheme + "//" + hostPort + "/tasks/jamp"; //wssUrl = "ws://localhost:8080/tasks/jamp"; return wssUrl; } function todo_createTask(taskName) { var myargs = new Array(); myargs.push(taskName); invokeJampMethod("/taskService", "addTask", showReturnFromServer, myargs); } function showReturnFromServer(data, call) { $("#tasks").prepend("<p style='color:blue'>" + data + "</p>"); var taskName = call.args[0]; $("#taskList").append("<p style='color:blue'>" + taskName + "</p>"); } function todo_openWebSocket() { todoService.webSocket = new WebSocket(todoService.webSocketURL); var socket = todoService.webSocket; socket.onmessage = function(msg) { todoService.onMessage(msg); } socket.onerror = function(errorEvent) { todoService.onError(errorEvent); } socket.onopen = function() { todoService.onOpen(); } socket.onclose = function(closeEvent) { todoService.onClose(closeEvent); } } function todo_onOpen() { todoService.status("Todo Client (logged in)"); $('#header').text( "Todo Client (logged in...)"); $("#inputArea").show(500); $("#statusBar").show(500); $("#todoNameInput").focus(); } function todo_Status(message) { $("#statusBar").show(500); $('#statusBarPara1').text(message); $("#statusBar").show(500); } function todo_onMessage(msgEvent) { $('#onMessageBar').text(msgEvent.data); var list = JSON.parse(msgEvent.data); if (list[0] == "reply") { replyRecieved(list[3], list[4], msgEvent.data); } else { } } function replyRecieved (qid, returnValue, data) { var call = calls[qid]; call.func(data, call); } function todo_onError(msg) { // $('#statusBarPara1').text(" Websocket error :" + JSON.stringfy(msg)); // $("#statusBar").show(500); } function todo_onClose(closeEvent) { $('#header').text( "Todo Client (not connected) " ); $('#statusBarPara1').text(" not logged in. " + ":: Reason: " + closeEvent.reason + " Code: " + closeEvent.code); $("#inputArea").hide(500); $("#statusBar").show(500); } todoService.open = todo_openWebSocket; todoService.onMessage = todo_onMessage; todoService.onOpen = todo_onOpen; todoService.onClose = todo_onClose; todoService.onError = todo_onError; todoService.createTask = todo_createTask; todoService.status = todo_Status; $(document).ready(function() { todoService.open(); $("#inputArea").show(); $("#statusBar").click(function() { $("#statusBar").hide(300); }); $("#todoNameInput").keypress(function(event) { var keycode = (event.keyCode ? event.keyCode : event.which); if (keycode == ENTER_KEY) { var textMessage = $("#todoNameInput").val(); if (textMessage=="bye!") { todoService.leaveChat(); } else { $("#todoNameInput").val(""); $("#hint").hide(500); todoService.createTask(textMessage); } } event.stopPropagation(); }); }); </script> </head> <body> <h1 id="header">Todo</h1> <div id="statusBar"> <p id="statusBarPara1">Welcome to Todo App, Click to hide</p> </div> <div id="onMessageBar"> <p id="onMessageBar1"></p> </div> <div id="inputArea"> <p id="hint">Enter a new task in here:</p> <input id="todoNameInput" type="text" value="" /> </div> <div id="tasksDiv"> <p id="tasks"></p> </div> <div id="taskListDiv"> <p id="taskList"></p> </div> </body> </html>
JAMP in a nutshell
["reply",{},"me",1,"buy milk--4566919316225680390-1371372542772964000-7"] ["query",{},"me",1,"/taskService","addTask","buy milk"] ["query",{},"me",2,"/taskService","addTask","call wife"] ["reply",{},"me",2,"call wife-7108026654197322644-1371372545793514000-8"]