package org.kevoree.library.rest; import org.kevoree.ContainerNode; import org.kevoree.ContainerRoot; import org.kevoree.Group; import org.kevoree.annotation.*; import org.kevoree.framework.*; import org.kevoree.framework.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.Option; import java.io.*; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by IntelliJ IDEA. * User: duke * Date: 11/10/11 * Time: 18:27 */ @DictionaryType({ @DictionaryAttribute(name = "port", defaultValue = "8000", optional = true, fragmentDependant = true), @DictionaryAttribute(name = "ip", defaultValue = "0.0.0.0", optional = true, fragmentDependant = true) }) @GroupType @Library(name = "JavaSE") public class RestGroup extends AbstractGroupType { private Logger logger = LoggerFactory.getLogger(this.getClass()); private ServerBootstrap server = new ServerBootstrap(this); protected ExecutorService poolUpdate; private boolean starting; @Start public void startRestGroup () { poolUpdate = Executors.newSingleThreadExecutor(); logger.warn("Rest service start on port " + this.getDictionary().get("port").toString()); Object ipOption = this.getDictionary().get("ip"); String ip = "0.0.0.0"; if (ipOption != null) { ip = ipOption.toString(); } server.startServer(Integer.parseInt(this.getDictionary().get("port").toString()), ip); //logger.info("!!! try to block => "+getModelService().getLastModel()+"->"+getModelService().getLastModification()); starting = true; } @Stop public void stopRestGroup () { server.stop(); poolUpdate.shutdownNow(); } @Override public void triggerModelUpdate () { if (starting) { final Option<ContainerRoot> modelOption = NodeNetworkHelper.updateModelWithNetworkProperty(this); if (modelOption.isDefined()) { new Thread() { public void run () { getModelService().unregisterModelListener(getModelListener()); getModelService().atomicUpdateModel(modelOption.get()); getModelService().registerModelListener(getModelListener()); } }.start(); } starting = false; } else { Group group = getModelElement(); for (ContainerNode subNode : group.getSubNodesForJ()) { if (!subNode.getName().equals(this.getNodeName())) { internalPush(getModelService().getLastModel(), subNode.getName(), this.getNodeName()); } } } } @Update public void updateRestGroup () { stopRestGroup(); startRestGroup(); } @Override public void push (ContainerRoot model, String targetNodeName) { List<String> ips = KevoreePropertyHelper.getStringNetworkProperties(model, targetNodeName, Constants.KEVOREE_PLATFORM_REMOTE_NODE_IP()); Option<Integer> portOption = KevoreePropertyHelper.getIntPropertyForGroup(model, this.getName(), "port", true, targetNodeName); int PORT = 8000; if (portOption.isDefined()) { PORT = portOption.get(); } boolean sent = false; for (String ip : ips) { logger.debug("try to send model on url=>" + "http://" + ip + ":" + PORT + "/model/current"); if (sendModel(model, "http://" + ip + ":" + PORT + "/model/current")) { sent = true; break; } } if (!sent) { logger.debug("try to send model on url=>" + "http://127.0.0.1:" + PORT + "/model/current"); if (!sendModel(model, "http://127.0.0.1:" + PORT + "/model/current")) { logger.debug("Unable to push a model on " + targetNodeName); } } } public void internalPush (ContainerRoot model, String targetNodeName, String sender) { List<String> ips = KevoreePropertyHelper.getStringNetworkProperties(model, targetNodeName, Constants.KEVOREE_PLATFORM_REMOTE_NODE_IP()); Option<Integer> portOption = KevoreePropertyHelper.getIntPropertyForGroup(model, this.getName(), "port", true, targetNodeName); int PORT = 8000; if (portOption.isDefined()) { PORT = portOption.get(); } boolean sent = false; for (String ip : ips) { logger.debug("try to send model on url=>" + "http://" + ip + ":" + PORT + "/model/current?sender=" + sender); if (sendModel(model, "http://" + ip + ":" + PORT + "/model/current?sender=" + sender)) { sent = true; break; } } if (!sent) { logger.debug("try to send model on url=>" + "http://127.0.0.1:" + PORT + "/model/current?sender=" + sender); if (!sendModel(model, "http://127.0.0.1:" + PORT + "/model/current?sender=" + sender)) { logger.debug("Unable to push a model on " + targetNodeName); } } } private boolean sendModel (ContainerRoot model, String urlPath) { try { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); KevoreeXmiHelper.saveStream(outStream, model); outStream.flush(); URL url = new URL(urlPath); URLConnection conn = url.openConnection(); conn.setConnectTimeout(3000); conn.setDoOutput(true); OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); wr.write(outStream.toString()); wr.flush(); // Get the response BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = rd.readLine(); while (line != null) { line = rd.readLine(); } wr.close(); rd.close(); return true; } catch (Exception e) { return false; } } @Override public ContainerRoot pull (String targetNodeName) { List<String> ips = KevoreePropertyHelper.getStringNetworkProperties(this.getModelService().getLastModel(), targetNodeName, Constants.KEVOREE_PLATFORM_REMOTE_NODE_IP()); Option<Integer> portOption = KevoreePropertyHelper.getIntPropertyForGroup(this.getModelService().getLastModel(), this.getName(), "port", true, targetNodeName); int PORT = 8000; if (portOption.isDefined()) { PORT = portOption.get(); } for (String ip : ips) { logger.debug("try to pull model on url=>" + "http://" + ip + ":" + PORT + "/model/current"); ContainerRoot model = pullModel("http://" + ip + ":" + PORT + "/model/current"); if (model != null) { return model; } } ContainerRoot model = pullModel("http://127.0.0.1:" + PORT + "/model/current"); if (model == null) { logger.debug("Unable to pull a model on " + targetNodeName); return null; } else { return model; } } private ContainerRoot pullModel (String urlPath) { try { URL url = new URL(urlPath); URLConnection conn = url.openConnection(); conn.setConnectTimeout(2000); InputStream inputStream = conn.getInputStream(); return KevoreeXmiHelper.loadStream(inputStream); } catch (IOException e) { return null; } } /** * <b>This method must only be use by RootService</b> * * @param model the new model to apply on the node * @param sender the sender name of the model (maybe an empty string if the sender is not a node of the group * @return the result may depend of the implementation. Basic implementation in RestGroup always returns <code>true</code> */ public boolean updateModel (final ContainerRoot model, final String sender) { Runnable t = new Runnable() { @Override public void run () { boolean externalSender = true; if (!sender.equals("")) { for (ContainerNode n : RestGroup.this.getModelElement().getSubNodesForJ()) { if (n.getName().equals(sender)) { externalSender = false; } } } if (!externalSender) { RestGroup.this.getModelService().unregisterModelListener(RestGroup.this.getModelListener()); } RestGroup.this.getModelService().atomicUpdateModel(model); if (!externalSender) { RestGroup.this.getModelService().registerModelListener(RestGroup.this.getModelListener()); } } }; poolUpdate.submit(t); logger.debug("Rest Group updateModel"); return true; } public String getModel () { return KevoreeXmiHelper.saveToString(this.getModelService().getLastModel(), false); } public RootService getRootService (String id) { return new RootService(id, this); } }