Java tutorial
/******************************************************************************* * Copyright 2014 Barcelona Supercomputing Center (BSC) * * 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. ******************************************************************************/ package com.servioticy.dispatcher; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.servioticy.datamodel.serviceobject.SO020; import com.servioticy.datamodel.serviceobject.SOChannel; import com.servioticy.datamodel.serviceobject.SOStream; import com.servioticy.datamodel.sensorupdate.SUChannel; import com.servioticy.datamodel.sensorupdate.SensorUpdate; import org.elasticsearch.common.geo.GeoPoint; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.IOException; import java.util.*; /** * @author ?lvaro Villalba Navarro <alvaro.villalba@bsc.es> */ public class SOProcessor020 extends SOProcessor { public static final int TYPE_SIMPLE = 0; public static final int TYPE_NUMBER = 0; public static final int TYPE_BOOLEAN = 1; public static final int TYPE_STRING = 2; public static final int TYPE_GEOPOINT = 3; public static final int TYPE_ARRAY = 4; public static final int TYPE_ARRAY_NUMBER = 4; public static final int TYPE_ARRAY_BOOLEAN = 5; public static final int TYPE_ARRAY_STRING = 6; public static final int TYPE_ARRAY_GEOPOINT = 7; public SO020 so; private ObjectMapper mapper; public SOProcessor020(SO020 so, ObjectMapper mapper) { this.mapper = mapper; this.so = so; } public static int findClosing(String str, int beginIndex, int endIndex, String closing) { String openKey = str.substring(beginIndex, endIndex); int strIndex = endIndex; int counter = 1; int beginCloseIndex = -1; while (counter > 0) { int beginOpenIndex = str.indexOf(openKey, strIndex); beginCloseIndex = str.indexOf(closing, strIndex); if (beginOpenIndex != -1 && beginOpenIndex < beginCloseIndex) { counter++; strIndex = beginOpenIndex + closing.length(); } else if (beginCloseIndex != -1 && (beginOpenIndex == -1 || beginCloseIndex < beginOpenIndex)) { counter--; strIndex = beginCloseIndex + closing.length(); } else { return -1; } } return beginCloseIndex; } public static boolean isFunction(String func) { if (func == null) { return false; } String trimmed = func.trim(); int beginArgsIndex = trimmed.indexOf("("); int endArgsIndex = findClosing(trimmed, beginArgsIndex, beginArgsIndex + 1, ")"); int beginFuncIndex = trimmed.indexOf("("); int endFuncIndex = findClosing(trimmed, beginFuncIndex, beginFuncIndex + 1, "}"); if (trimmed.indexOf("function") < beginArgsIndex && beginArgsIndex < trimmed.indexOf(')') && endArgsIndex != -1 && endArgsIndex < beginFuncIndex && beginFuncIndex < trimmed.indexOf('}') && endFuncIndex != -1 && (trimmed.indexOf(';') == -1 || trimmed.indexOf(';') > beginFuncIndex) && endFuncIndex == (trimmed.length() - 1)) { return true; } return false; } public static String functionArgsString(String func) { int beginIndex = func.indexOf('('); int endIndex = findClosing(func, beginIndex, beginIndex + 1, ")"); return func.substring(beginIndex + 1, endIndex).trim(); } public static List<String> functionArgs(String func) { String argsStr = functionArgsString(func); if (argsStr == null) { return null; } ArrayList<String> args = new ArrayList<String>(Arrays.asList(argsStr.split(","))); for (String arg : args) { arg = arg.trim(); } return args; } public Set<String> getStreamsBySourceId(String sourceId) { Set<String> streams = new HashSet<String>(); for (Map.Entry<String, SOStream> streamEntry : this.so.getStreams(this.mapper).entrySet()) { String streamId = streamEntry.getKey(); SOStream stream = streamEntry.getValue(); for (Map.Entry<String, SOChannel> channelEntry : stream.getChannels().entrySet()) { String channelId = channelEntry.getKey(); SOChannel channel = channelEntry.getValue(); String cvFunction = channel.getCurrentValue(); if (cvFunction == null) { continue; } for (String arg : functionArgs(cvFunction)) { if (arg.equals(sourceId)) { streams.add(streamId); } } } } return streams; } public Set<String> getSourceIdsByStream(String streamId) { Set<String> sourceIds = new HashSet<String>(); SOStream stream = this.so.getStreams(this.mapper).get(streamId); for (Map.Entry<String, SOChannel> channelEntry : stream.getChannels().entrySet()) { String channelId = channelEntry.getKey(); SOChannel channel = channelEntry.getValue(); sourceIds.addAll(functionArgs(channel.getCurrentValue())); } return sourceIds; } public String initializationCode(Map<String, SensorUpdate> sensorUpdates, String origin) throws JsonProcessingException { String result = ""; for (Map.Entry<String, SensorUpdate> suEntry : sensorUpdates.entrySet()) { result += "var " + suEntry.getKey() + " = " + this.mapper.writeValueAsString(suEntry.getValue()) + ";"; } // $input needs to be EXACTLY the same object as the origin one if (sensorUpdates.get("$input") == null) { result += "var $input=" + origin + ";"; } return result; } private int parseType(String type) { type = type.toLowerCase().trim(); int typeCode = TYPE_SIMPLE; if (type.startsWith("array")) { String arrayType = type.substring("array".length(), type.length()); if (arrayType == null) { return -1; } arrayType = arrayType.trim(); if (!arrayType.startsWith("[") || !arrayType.endsWith("]")) { return -1; } arrayType = arrayType.substring(1, arrayType.length() - 1); if (arrayType == null) { return -1; } arrayType = arrayType.trim(); type = arrayType; typeCode = TYPE_ARRAY; } if (type.equals("number")) { return typeCode + TYPE_NUMBER; } else if (type.equals("boolean")) { return typeCode + TYPE_BOOLEAN; } else if (type.equals("string")) { return typeCode + TYPE_STRING; } else if (type.equals("geo_point")) { return typeCode + TYPE_GEOPOINT; } return -1; } public SensorUpdate getResultSU(String streamId, Map<String, SensorUpdate> inputSUs, String origin, long timestamp) throws IOException, ScriptException { ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("JavaScript"); SensorUpdate su = new SensorUpdate(); su.setLastUpdate(timestamp); su.setChannels(new LinkedHashMap<String, SUChannel>()); SOStream stream = so.getStreams(this.mapper).get(streamId); for (Map.Entry<String, SOChannel> channelEntry : stream.getChannels().entrySet()) { SOChannel channel = channelEntry.getValue(); SUChannel suChannel = new SUChannel(); String currentValueCode = channel.getCurrentValue(); // TODO Check for invalid calls // TODO Check for an invalid header if (isFunction(currentValueCode)) { // There is no code for one of the channels. Invalid. return null; } else { TypeReference type; Class dataClass; boolean array = false; switch (parseType(channel.getType())) { case TYPE_ARRAY_NUMBER: type = new TypeReference<List<Double>>() { }; array = true; break; case TYPE_NUMBER: type = new TypeReference<Double>() { }; break; case TYPE_ARRAY_BOOLEAN: type = new TypeReference<List<Boolean>>() { }; array = true; break; case TYPE_BOOLEAN: type = new TypeReference<Boolean>() { }; break; case TYPE_ARRAY_STRING: type = new TypeReference<List<String>>() { }; array = true; break; case TYPE_STRING: type = new TypeReference<String>() { }; break; // non-primitive types case TYPE_ARRAY_GEOPOINT: type = new TypeReference<List<GeoPoint>>() { }; array = true; break; case TYPE_GEOPOINT: type = new TypeReference<GeoPoint>() { }; break; default: return null; } String resultVar = "$" + Long.toHexString(UUID.randomUUID().getMostSignificantBits()); engine.eval(initializationCode(inputSUs, origin) + "var " + resultVar + " = JSON.stringify(" + currentValueCode + "(" + functionArgsString(currentValueCode) + ")" + ")"); Object result = this.mapper.readValue((String) engine.get(resultVar), type); if (result == null) { // Filtered output. The type is not the expected one. return null; } suChannel.setCurrentValue(result); } suChannel.setUnit(channel.getUnit()); su.getChannels().put(channelEntry.getKey(), suChannel); } su.setTriggerPath(new ArrayList<ArrayList<String>>()); su.setPathTimestamps(new ArrayList<Long>()); return su; } }