/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*/
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.thingml.networkplugins.java;
import org.apache.commons.io.IOUtils;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.sintef.thingml.*;
import org.sintef.thingml.helpers.AnnotatedElementHelper;
import org.thingml.compilers.Context;
import org.thingml.compilers.spi.NetworkPlugin;
import org.thingml.compilers.spi.SerializationPlugin;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class JavaMQTTPlugin extends NetworkPlugin {
public JavaMQTTPlugin() {
super();
}
public String getPluginID() {
return "JavaMQTTPlugin";
}
public List<String> getSupportedProtocols() {
List<String> res = new ArrayList<>();
res.add("MQTT");
res.add("mqtt");
return res;
}
public List<String> getTargetedLanguages() {
List<String> res = new ArrayList<>();
res.add("java");
return res;
}
private Set<Message> messages = new HashSet<Message>();
private void clearMessages() {
messages.clear();
}
private void addMessage(Message m) {
boolean contains = false;
for(Message msg : messages) {
if (EcoreUtil.equals(msg, m)) {
contains = true;
break;
}
}
if (!contains) {
messages.add(m);
}
}
private void updatePOM(Context ctx) {
//Update POM.xml with JSSC Maven dependency
try {
final InputStream input = new FileInputStream(ctx.getOutputDirectory() + "/pom.xml");
final List<String> packLines = IOUtils.readLines(input, Charset.forName("UTF-8"));
String pom = "";
for (String line : packLines) {
pom += line + "\n";
}
input.close();
pom = pom.replace("<!--DEP-->", "<dependency>\n" +
" <groupId>org.fusesource.mqtt-client</groupId>\n" +
" <artifactId>mqtt-client</artifactId>\n" +
" <version>1.12</version>\n" +
"</dependency>\n<!--DEP-->");
final File f = new File(ctx.getOutputDirectory() + "/pom.xml");
final OutputStream output = new FileOutputStream(f);
IOUtils.write(pom, output, Charset.forName("UTF-8"));
IOUtils.closeQuietly(output);
} catch (Exception e) {
e.printStackTrace();
}
}
public void generateNetworkLibrary(Configuration cfg, Context ctx, Set<Protocol> protocols) {
boolean escape = false; //FIXME true/false depending on annotation
updatePOM(ctx);
StringBuilder builder = new StringBuilder();
for (Protocol prot : protocols) {
clearMessages();
for (ThingPortMessage tpm : getMessagesSent(cfg, prot)) {
addMessage(tpm.m);
}
SerializationPlugin sp = null;
try {
sp = ctx.getSerializationPlugin(prot);
} catch (UnsupportedEncodingException uee) {
System.err.println("Could not get serialization plugin... Expect some errors in the generated code");
uee.printStackTrace();
return;
}
StringBuilder temp = new StringBuilder();
if (sp.getSupportedFormat().contains("Binary")) {
temp.append("public Byte[] format(Event e){\n");
} else {
temp.append("public String format(Event e){\n");
}
int i = 0;
for(Message m : messages) {
if (i > 0)
temp.append("else ");
temp.append("if (e.getType().equals(" + m.getName().toUpperCase() + ")) {\n");
temp.append("return ");
if (sp.getSupportedFormat().contains("Binary")) {//FIXME
temp.append("JavaBinaryHelper.toObject(");
if (escape) {
temp.append("JavaBinaryHelper.unescape(");
}
}
temp.append("format((" + ctx.firstToUpper(m.getName()) + "MessageType." + ctx.firstToUpper(m.getName()) + "Message)e)\n");
if (sp.getSupportedFormat().contains("Binary")) {//FIXME
temp.append(")");
if (escape) {
temp.append(")");
}
}
temp.append(";");
temp.append("}\n");
i++;
}
temp.append("return null;\n");
temp.append("}\n");
for (Message m : messages) {
sp.generateSerialization(temp, prot.getName() + "BinaryProtocol", m);
}
clearMessages();
builder = new StringBuilder();
for (ThingPortMessage tpm : getMessagesReceived(cfg, prot)) {
addMessage(tpm.m);
}
sp.generateParserBody(builder, prot.getName() + "BinaryProtocol", null, messages, null);
final String result = builder.toString().replace("/*$SERIALIZERS$*/", temp.toString());
try {
final File folder = new File(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/network");
folder.mkdir();
final File f = new File(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/network/" + prot.getName() + "BinaryProtocol.java");
final OutputStream output = new FileOutputStream(f);
IOUtils.write(result, output, Charset.forName("UTF-8"));
IOUtils.closeQuietly(output);
} catch (Exception e) {
e.printStackTrace();
}
new SerialProtocol(ctx, prot, cfg).generate(escape);
}
}
private class SerialProtocol {
Context ctx;
Protocol prot;
Configuration cfg;
private List<Port> ports = new ArrayList<Port>();
public SerialProtocol(Context ctx, Protocol prot, Configuration cfg) {
this.ctx = ctx;
this.prot = prot;
this.cfg = cfg;
}
private void addPort(Port p) {
boolean contains = false;
for (Port port : ports) {
if (EcoreUtil.equals(port, p)) {
contains = true;
break;
}
}
if (!contains) {
ports.add(p);
}
}
public void generate(boolean escape) {
for (ThingPortMessage tpm : getMessagesSent(cfg, prot)) {
addPort(tpm.p);
}
for (ThingPortMessage tpm : getMessagesReceived(cfg, prot)) {
addPort(tpm.p);
}
String template = ctx.getTemplateByID("templates/JavaMQTTPlugin.java");
template = template.replace("/*$SERIALIZER$*/", prot.getName() + "BinaryProtocol");
StringBuilder parseBuilder = new StringBuilder();
SerializationPlugin sp = null;
try {
sp = ctx.getSerializationPlugin(prot);
} catch (UnsupportedEncodingException uee) {
System.err.println("Could not get serialization plugin... Expect some errors in the generated code");
uee.printStackTrace();
return;
}
if (sp.getSupportedFormat().contains("Binary")) {//FIXME
parseBuilder.append("final Event event = formatter.instantiate(JavaBinaryHelper.toObject(payload.toByteArray()));\n");
if (escape) {
template = template.replace("/*$PUBLISH$*/", "connection.publish(this.pubtopic, JavaBinaryHelper.escape(JavaBinaryHelper.toPrimitive((Byte[]) payload)), QoS.AT_LEAST_ONCE, false, disconnectOnFailure);\n");
} else {
template = template.replace("/*$PUBLISH$*/", "connection.publish(this.pubtopic, JavaBinaryHelper.toPrimitive((Byte[]) payload), QoS.AT_LEAST_ONCE, false, disconnectOnFailure);\n");
}
} else {
parseBuilder.append("final Event event = formatter.instantiate(new String(payload.toByteArray(), java.nio.charset.StandardCharsets.UTF_8));\n");
template = template.replace("/*$PUBLISH$*/", "connection.publish(new UTF8Buffer(this.pubtopic), new Buffer(((String)payload).getBytes()), QoS.AT_LEAST_ONCE, false, disconnectOnFailure);\n");
}
for(Port p : ports) {//FIXME
parseBuilder.append("if (event != null) " + p.getName() + "_port.send(event);\n");
}
template = initPort(ctx, template);
for (ExternalConnector conn : getExternalConnectors(cfg, prot)) {
updateMain(ctx, cfg, conn);
}
template = template.replace("/*$PARSING CODE$*/", parseBuilder.toString());
try {
final File folder = new File(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/network");
folder.mkdir();
final File f = new File(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/network/MQTTJava.java");
final OutputStream output = new FileOutputStream(f);
IOUtils.write(template, output, Charset.forName("UTF-8"));
IOUtils.closeQuietly(output);
} catch (Exception e) {
e.printStackTrace();
}
}
private String initPort(Context ctx, String template) {
for (Port p : ports) {
template = template.replace("/*$PORTS$*/", "/*$PORTS$*/\nprivate Port " + p.getName() + "_port;\npublic Port get" + ctx.firstToUpper(p.getName()) + "_port(){return " + p.getName() + "_port;}\n");
String portType = "PortType.PROVIDED";
if (p instanceof RequiredPort)
portType = "PortType.REQUIRED";
template = template.replace("/*$INIT PORTS$*/", "/*$INIT PORTS$*/\n" + p.getName() + "_port = new Port(" + portType + ", \"" + p.getName() + "\", this);\n");
}
return template;
}
private void updateMain(Context ctx, Configuration cfg, ExternalConnector conn) {
try {
final InputStream input = new FileInputStream(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/Main.java");
final List<String> packLines = IOUtils.readLines(input);
String main = "";
for (String line : packLines) {
main += line + "\n";
}
input.close();
final String url = "tcp://" + AnnotatedElementHelper.annotationOrElse(conn, "url", AnnotatedElementHelper.annotationOrElse(conn.getProtocol(), "url", "127.0.0.1:1883"));
final String subtopic = AnnotatedElementHelper.annotationOrElse(conn.getProtocol(), "subscribe", "ThingML");
final String pubtopic = AnnotatedElementHelper.annotationOrElse(conn.getProtocol(), "publish", "ThingML");
main = main.replace("/*$NETWORK$*/", "/*$NETWORK$*/\nMQTTJava " + conn.getName() + "_" + conn.getProtocol().getName() + " = (MQTTJava) new MQTTJava(\"" + url + "\", \"" + pubtopic + "\", \"" + subtopic + "\").buildBehavior(null, null);\n");
StringBuilder connBuilder = new StringBuilder();
connBuilder.append(conn.getName() + "_" + conn.getProtocol().getName() + ".get" + ctx.firstToUpper(conn.getPort().getName()) + "_port().addListener(");
connBuilder.append(ctx.getInstanceName(conn.getInst().getInstance()) + ".get" + ctx.firstToUpper(conn.getPort().getName()) + "_port());\n");
connBuilder.append(ctx.getInstanceName(conn.getInst().getInstance()) + ".get" + ctx.firstToUpper(conn.getPort().getName()) + "_port().addListener(");
connBuilder.append(conn.getName() + "_" + conn.getProtocol().getName() + ".get" + ctx.firstToUpper(conn.getPort().getName()) + "_port());\n");
main = main.replace("/*$EXT CONNECTORS$*/", "/*$EXT CONNECTORS$*/\n" + connBuilder.toString());
main = main.replace("/*$START$*/", "/*$START$*/\n" + conn.getName() + "_" + conn.getProtocol().getName() + ".init().start();\n");
main = main.replace("/*$STOP$*/", "/*$STOP$*/\n" + conn.getName() + "_" + conn.getProtocol().getName() + ".stop();\n");
final File f = new File(ctx.getOutputDirectory() + "/src/main/java/org/thingml/generated/Main.java");
final OutputStream output = new FileOutputStream(f);
IOUtils.write(main, output, Charset.forName("UTF-8"));
IOUtils.closeQuietly(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}