diff --git a/WebContent/WEB-INF/dwr.xml b/WebContent/WEB-INF/dwr.xml index 2d6c68637b..a315a7e35b 100644 --- a/WebContent/WEB-INF/dwr.xml +++ b/WebContent/WEB-INF/dwr.xml @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. --> - + diff --git a/WebContent/WEB-INF/lib/dwr.jar b/WebContent/WEB-INF/lib/dwr.jar index 8444d5205b..315a5118da 100644 Binary files a/WebContent/WEB-INF/lib/dwr.jar and b/WebContent/WEB-INF/lib/dwr.jar differ diff --git a/WebContent/resources/common.js b/WebContent/resources/common.js index 49dba3ca4b..a0187d5aa6 100644 --- a/WebContent/resources/common.js +++ b/WebContent/resources/common.js @@ -23,6 +23,25 @@ var mango = {}; var lasTimeUpdate; +// DWR 2 compatibility: global $() alias. +(function () { + if (typeof window.$ === 'function') { + return; + } + + if (window.dwr && dwr.util && typeof dwr.util.byId === 'function') { + window.$ = dwr.util.byId; + return; + } + + window.$ = function (id) { + if (typeof id === 'string') { + return document.getElementById(id); + } + return id; + }; +})(); + // // String prototypes // diff --git a/src/com/serotonin/mango/web/dwr/OpcUaDataTypeConverter.java b/src/com/serotonin/mango/web/dwr/OpcUaDataTypeConverter.java index dc645f496d..0ed6047f68 100644 --- a/src/com/serotonin/mango/web/dwr/OpcUaDataTypeConverter.java +++ b/src/com/serotonin/mango/web/dwr/OpcUaDataTypeConverter.java @@ -9,8 +9,8 @@ public class OpcUaDataTypeConverter extends EnumConverter { @Override - public Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx) throws MarshallException { - String value = LocalUtil.decode(iv.getValue()); + public Object convertInbound(Class paramType, InboundVariable iv) throws MarshallException { + String value = LocalUtil.urlDecode(iv.getValue()); try { return OpcUaDataType.valueByNameOf(value); } catch (Exception var9) { diff --git a/src/com/serotonin/mango/web/dwr/ProtocolVersionConverter.java b/src/com/serotonin/mango/web/dwr/ProtocolVersionConverter.java index 50cadcd3e6..92223e2a72 100644 --- a/src/com/serotonin/mango/web/dwr/ProtocolVersionConverter.java +++ b/src/com/serotonin/mango/web/dwr/ProtocolVersionConverter.java @@ -9,8 +9,8 @@ public class ProtocolVersionConverter extends EnumConverter { @Override - public Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx) throws MarshallException { - String value = LocalUtil.decode(iv.getValue()); + public Object convertInbound(Class paramType, InboundVariable iv) throws MarshallException { + String value = LocalUtil.urlDecode(iv.getValue()); try { return ProtocolVersion.protocolVersion(value); } catch (Exception var9) { diff --git a/src/com/serotonin/mango/web/filter/WebContextFilter.java b/src/com/serotonin/mango/web/filter/WebContextFilter.java index 00f4be8885..7bd8850128 100644 --- a/src/com/serotonin/mango/web/filter/WebContextFilter.java +++ b/src/com/serotonin/mango/web/filter/WebContextFilter.java @@ -23,41 +23,33 @@ import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.directwebremoting.impl.DefaultWebContextBuilder; +import org.directwebremoting.servlet.DwrWebContextFilter; /** * @author Matthew Lohbihler */ public class WebContextFilter implements Filter { - private final DefaultWebContextBuilder builder = new DefaultWebContextBuilder(); - private ServletContext servletContext; + + private final DwrWebContextFilter delegate = new DwrWebContextFilter(); + @Override - public void init(FilterConfig config) { - servletContext = config.getServletContext(); + public void init(FilterConfig config) throws ServletException { + delegate.init(config); } @Override public void destroy() { - // no op + delegate.destroy(); } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, - ServletException { - try { - builder.set((HttpServletRequest) request, (HttpServletResponse) response, null, servletContext, null); - chain.doFilter(request, response); - } - finally { - builder.unset(); - } + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + delegate.doFilter(request, response, chain); } } diff --git a/src/com/serotonin/web/taglib/DwrConvertTag.java b/src/com/serotonin/web/taglib/DwrConvertTag.java new file mode 100644 index 0000000000..aa4e0d3884 --- /dev/null +++ b/src/com/serotonin/web/taglib/DwrConvertTag.java @@ -0,0 +1,229 @@ +package com.serotonin.web.taglib; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.TagSupport; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.ResourceBundle; + +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.serotonin.web.i18n.I18NUtils; +import com.serotonin.web.i18n.LocalizableMessage; + +public class DwrConvertTag extends TagSupport { + + private static final ObjectMapper JSON_MAPPER = buildObjectMapper(); + + private Object obj; + + public DwrConvertTag() { + // no-op + } + + public void setObj(Object obj) { + this.obj = obj; + } + + @Override + public int doStartTag() throws JspException { + try { + JspWriter out = pageContext.getOut(); + String json = toJson(obj); + out.write(json); + return SKIP_BODY; + } catch (IOException e) { + throw new JspException("Error writing DwrConvertTag JSON content", e); + } + } + + @Override + public void release() { + super.release(); + this.obj = null; + } + + private String toJson(Object value) { + if (value == null) { + return "null"; + } + + if (value instanceof Boolean) { + return writeJsonValue(value); + } + + if (value instanceof Number) { + return numberToJson((Number) value); + } + + if (value instanceof CharSequence) { + return writeJsonValue(value.toString()); + } + + if (value instanceof LocalizableMessage) { + String localized = resolveLocalizableMessage((LocalizableMessage) value); + return writeJsonValue(localized); + } + + if (value instanceof Map) { + return mapToJson((Map) value); + } + + if (value instanceof Collection) { + return collectionToJson((Collection) value); + } + + if (value.getClass().isArray()) { + return arrayToJson(value); + } + + return writeJsonValue(value); + } + + private String resolveLocalizableMessage(LocalizableMessage lm) { + try { + if (pageContext == null) { + return lm.getLocalizedMessage(null); + } + + Object req = pageContext.getRequest(); + if (req instanceof HttpServletRequest) { + HttpServletRequest httpReq = (HttpServletRequest) req; + ResourceBundle bundle = I18NUtils.getBundle(httpReq); + return lm.getLocalizedMessage(bundle); + } + + return lm.getLocalizedMessage(null); + } catch (Exception e) { + String key = lm.getKey(); + return key != null ? key : lm.serialize(); + } + } + + private String mapToJson(Map map) { + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Iterator> it = map.entrySet().iterator(); + boolean first = true; + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (!first) { + sb.append(','); + } + first = false; + + String key = String.valueOf(entry.getKey()); + Object val = entry.getValue(); + + sb.append(writeJsonValue(key)); + sb.append(':'); + sb.append(toJson(val)); + } + sb.append('}'); + return sb.toString(); + } + + private String collectionToJson(Collection collection) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = collection.iterator(); + boolean first = true; + while (it.hasNext()) { + if (!first) { + sb.append(','); + } + first = false; + Object val = it.next(); + sb.append(toJson(val)); + } + sb.append(']'); + return sb.toString(); + } + + private String arrayToJson(Object array) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + int length = Array.getLength(array); + for (int i = 0; i < length; i++) { + if (i > 0) { + sb.append(','); + } + Object val = Array.get(array, i); + sb.append(toJson(val)); + } + sb.append(']'); + return sb.toString(); + } + + private String numberToJson(Number number) { + if (number instanceof Double) { + double value = (Double) number; + if (Double.isNaN(value) || Double.isInfinite(value)) { + return "null"; + } + } else if (number instanceof Float) { + float value = (Float) number; + if (Float.isNaN(value) || Float.isInfinite(value)) { + return "null"; + } + } + return writeJsonValue(number); + } + + private String writeJsonValue(Object value) { + try { + return JSON_MAPPER.writeValueAsString(value); + } catch (IOException e) { + throw new IllegalArgumentException("Failed to serialize value for DwrConvertTag", e); + } + } + + private static ObjectMapper buildObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.getFactory().setCharacterEscapes(HtmlSafeCharacterEscapes.INSTANCE); + return mapper; + } + + private static final class HtmlSafeCharacterEscapes extends CharacterEscapes { + private static final long serialVersionUID = 1L; + private static final HtmlSafeCharacterEscapes INSTANCE = new HtmlSafeCharacterEscapes(); + private static final SerializableString ESC_LT = new SerializedString("\\u003c"); + private static final SerializableString ESC_GT = new SerializedString("\\u003e"); + private static final SerializableString ESC_AMP = new SerializedString("\\u0026"); + + private final int[] escapeCodes; + + private HtmlSafeCharacterEscapes() { + escapeCodes = CharacterEscapes.standardAsciiEscapesForJSON(); + escapeCodes['<'] = CharacterEscapes.ESCAPE_CUSTOM; + escapeCodes['>'] = CharacterEscapes.ESCAPE_CUSTOM; + escapeCodes['&'] = CharacterEscapes.ESCAPE_CUSTOM; + } + + @Override + public int[] getEscapeCodesForAscii() { + return escapeCodes; + } + + @Override + public SerializableString getEscapeSequence(int ch) { + if (ch == '<') { + return ESC_LT; + } + if (ch == '>') { + return ESC_GT; + } + if (ch == '&') { + return ESC_AMP; + } + return null; + } + } +}