http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/README ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/README b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/README new file mode 100644 index 0000000..2d7cdcf --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/README @@ -0,0 +1,68 @@ +JSON in Java [package com.vmware.gemfire.tools.pulse.internal.json] + +Douglas Crockford +doug...@crockford.com + +2011-02-02 + + +JSON is a light-weight, language independent, data interchange format. +See http://www.JSON.org/ + +The files in this package implement JSON encoders/decoders in Java. +It also includes the capability to convert between JSON and XML, HTTP +headers, Cookies, and CDL. + +This is a reference implementation. There is a large number of JSON packages +in Java. Perhaps someday the Java community will standardize on one. Until +then, choose carefully. + +The license includes this restriction: "The software shall be used for good, +not evil." If your conscience cannot live with that, then choose a different +package. + +The package compiles on Java 1.2 thru Java 1.4. + + +JSONObject.java: The JSONObject can parse text from a String or a JSONTokener +to produce a map-like object. The object provides methods for manipulating its +contents, and for producing a JSON compliant object serialization. + +JSONArray.java: The JSONObject can parse text from a String or a JSONTokener +to produce a vector-like object. The object provides methods for manipulating +its contents, and for producing a JSON compliant array serialization. + +JSONTokener.java: The JSONTokener breaks a text into a sequence of individual +tokens. It can be constructed from a String, Reader, or InputStream. + +JSONException.java: The JSONException is the standard exception type thrown +by this package. + + +JSONString.java: The JSONString interface requires a toJSONString method, +allowing an object to provide its own serialization. + +JSONStringer.java: The JSONStringer provides a convenient facility for +building JSON strings. + +JSONWriter.java: The JSONWriter provides a convenient facility for building +JSON text through a writer. + + +CDL.java: CDL provides support for converting between JSON and comma +delimited lists. + +Cookie.java: Cookie provides support for converting between JSON and cookies. + +CookieList.java: CookieList provides support for converting between JSON and +cookie lists. + +HTTP.java: HTTP provides support for converting between JSON and HTTP headers. + +HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers. + +XML.java: XML provides support for converting between JSON and XML. + +JSONML.java: JSONML provides support for converting between JSONML and XML. + +XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text. \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XML.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XML.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XML.java new file mode 100644 index 0000000..d43eb77 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XML.java @@ -0,0 +1,503 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.json; + +import java.util.Iterator; + + +/** + * This provides static methods to convert an XML text into a JSONObject, + * and to covert a JSONObject into an XML text. + * @author JSON.org + * @version 2011-02-11 + */ +public class XML { + + /** The Character '&'. */ + public static final Character AMP = new Character('&'); + + /** The Character '''. */ + public static final Character APOS = new Character('\''); + + /** The Character '!'. */ + public static final Character BANG = new Character('!'); + + /** The Character '='. */ + public static final Character EQ = new Character('='); + + /** The Character '>'. */ + public static final Character GT = new Character('>'); + + /** The Character '<'. */ + public static final Character LT = new Character('<'); + + /** The Character '?'. */ + public static final Character QUEST = new Character('?'); + + /** The Character '"'. */ + public static final Character QUOT = new Character('"'); + + /** The Character '/'. */ + public static final Character SLASH = new Character('/'); + + /** + * Replace special characters with XML escapes: + * <pre> + * & <small>(ampersand)</small> is replaced by &amp; + * < <small>(less than)</small> is replaced by &lt; + * > <small>(greater than)</small> is replaced by &gt; + * " <small>(double quote)</small> is replaced by &quot; + * </pre> + * @param string The string to be escaped. + * @return The escaped string. + */ + public static String escape(String string) { + StringBuffer sb = new StringBuffer(); + for (int i = 0, length = string.length(); i < length; i++) { + char c = string.charAt(i); + switch (c) { + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '"': + sb.append("""); + break; + case '\'': + sb.append("'"); + break; + default: + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Throw an exception if the string contains whitespace. + * Whitespace is not allowed in tagNames and attributes. + * @param string + * @throws JSONException + */ + public static void noSpace(String string) throws JSONException { + int i, length = string.length(); + if (length == 0) { + throw new JSONException("Empty string."); + } + for (i = 0; i < length; i += 1) { + if (Character.isWhitespace(string.charAt(i))) { + throw new JSONException("'" + string + + "' contains a space character."); + } + } + } + + /** + * Scan the content following the named tag, attaching it to the context. + * @param x The XMLTokener containing the source string. + * @param context The JSONObject that will include the new material. + * @param name The tag name. + * @return true if the close tag is processed. + * @throws JSONException + */ + private static boolean parse(XMLTokener x, JSONObject context, + String name) throws JSONException { + char c; + int i; + JSONObject jsonobject = null; + String string; + String tagName; + Object token; + +// Test for and skip past these forms: +// <!-- ... --> +// <! ... > +// <![ ... ]]> +// <? ... ?> +// Report errors for these forms: +// <> +// <= +// << + + token = x.nextToken(); + +// <! + + if (token == BANG) { + c = x.next(); + if (c == '-') { + if (x.next() == '-') { + x.skipPast("-->"); + return false; + } + x.back(); + } else if (c == '[') { + token = x.nextToken(); + if ("CDATA".equals(token)) { + if (x.next() == '[') { + string = x.nextCDATA(); + if (string.length() > 0) { + context.accumulate("content", string); + } + return false; + } + } + throw x.syntaxError("Expected 'CDATA['"); + } + i = 1; + do { + token = x.nextMeta(); + if (token == null) { + throw x.syntaxError("Missing '>' after '<!'."); + } else if (token == LT) { + i += 1; + } else if (token == GT) { + i -= 1; + } + } while (i > 0); + return false; + } else if (token == QUEST) { + +// <? + + x.skipPast("?>"); + return false; + } else if (token == SLASH) { + +// Close tag </ + + token = x.nextToken(); + if (name == null) { + throw x.syntaxError("Mismatched close tag " + token); + } + if (!token.equals(name)) { + throw x.syntaxError("Mismatched " + name + " and " + token); + } + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped close tag"); + } + return true; + + } else if (token instanceof Character) { + throw x.syntaxError("Misshaped tag"); + +// Open tag < + + } else { + tagName = (String)token; + token = null; + jsonobject = new JSONObject(); + for (;;) { + if (token == null) { + token = x.nextToken(); + } + +// attribute = value + + if (token instanceof String) { + string = (String)token; + token = x.nextToken(); + if (token == EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + jsonobject.accumulate(string, + XML.stringToValue((String)token)); + token = null; + } else { + jsonobject.accumulate(string, ""); + } + +// Empty tag <.../> + + } else if (token == SLASH) { + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + if (jsonobject.length() > 0) { + context.accumulate(tagName, jsonobject); + } else { + context.accumulate(tagName, ""); + } + return false; + +// Content, between <...> and </...> + + } else if (token == GT) { + for (;;) { + token = x.nextContent(); + if (token == null) { + if (tagName != null) { + throw x.syntaxError("Unclosed tag " + tagName); + } + return false; + } else if (token instanceof String) { + string = (String)token; + if (string.length() > 0) { + jsonobject.accumulate("content", + XML.stringToValue(string)); + } + +// Nested element + + } else if (token == LT) { + if (parse(x, jsonobject, tagName)) { + if (jsonobject.length() == 0) { + context.accumulate(tagName, ""); + } else if (jsonobject.length() == 1 && + jsonobject.opt("content") != null) { + context.accumulate(tagName, + jsonobject.opt("content")); + } else { + context.accumulate(tagName, jsonobject); + } + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. This is much less ambitious than + * JSONObject.stringToValue, especially because it does not attempt to + * convert plus forms, octal forms, hex forms, or E forms lacking decimal + * points. + * @param string A String. + * @return A simple JSON value. + */ + public static Object stringToValue(String string) { + if ("".equals(string)) { + return string; + } + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + if ("null".equalsIgnoreCase(string)) { + return JSONObject.NULL; + } + if ("0".equals(string)) { + return new Integer(0); + } + +// If it might be a number, try converting it. If that doesn't work, +// return the string. + + try { + char initial = string.charAt(0); + boolean negative = false; + if (initial == '-') { + initial = string.charAt(1); + negative = true; + } + if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') { + return string; + } + if ((initial >= '0' && initial <= '9')) { + if (string.indexOf('.') >= 0) { + return Double.valueOf(string); + } else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) { + Long myLong = new Long(string); + if (myLong.longValue() == myLong.intValue()) { + return new Integer(myLong.intValue()); + } else { + return myLong; + } + } + } + } catch (Exception ignore) { + } + return string; + } + + + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONObject. Some information may be lost in this transformation + * because JSON is a data format and XML is a document format. XML uses + * elements, attributes, and content text, while JSON uses unordered + * collections of name/value pairs and arrays of values. JSON does not + * does not like to distinguish between elements and attributes. + * Sequences of similar elements are represented as JSONArrays. Content + * text may be placed in a "content" member. Comments, prologs, DTDs, and + * <code><[ [ ]]></code> are ignored. + * @param string The source string. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(string); + while (x.more() && x.skipPast("<")) { + parse(x, jo, null); + } + return jo; + } + + + /** + * Convert a JSONObject into a well-formed, element-normal XML string. + * @param object A JSONObject. + * @return A string. + * @throws JSONException + */ + public static String toString(Object object) throws JSONException { + return toString(object, null); + } + + + /** + * Convert a JSONObject into a well-formed, element-normal XML string. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @return A string. + * @throws JSONException + */ + public static String toString(Object object, String tagName) + throws JSONException { + StringBuffer sb = new StringBuffer(); + int i; + JSONArray ja; + JSONObject jo; + String key; + Iterator keys; + int length; + String string; + Object value; + if (object instanceof JSONObject) { + +// Emit <tagName> + + if (tagName != null) { + sb.append('<'); + sb.append(tagName); + sb.append('>'); + } + +// Loop thru the keys. + + jo = (JSONObject)object; + keys = jo.keys(); + while (keys.hasNext()) { + key = keys.next().toString(); + value = jo.opt(key); + if (value == null) { + value = ""; + } + if (value instanceof String) { + string = (String)value; + } else { + string = null; + } + +// Emit content in body + + if ("content".equals(key)) { + if (value instanceof JSONArray) { + ja = (JSONArray)value; + length = ja.length(); + for (i = 0; i < length; i += 1) { + if (i > 0) { + sb.append('\n'); + } + sb.append(escape(ja.get(i).toString())); + } + } else { + sb.append(escape(value.toString())); + } + +// Emit an array of similar keys + + } else if (value instanceof JSONArray) { + ja = (JSONArray)value; + length = ja.length(); + for (i = 0; i < length; i += 1) { + value = ja.get(i); + if (value instanceof JSONArray) { + sb.append('<'); + sb.append(key); + sb.append('>'); + sb.append(toString(value)); + sb.append("</"); + sb.append(key); + sb.append('>'); + } else { + sb.append(toString(value, key)); + } + } + } else if ("".equals(value)) { + sb.append('<'); + sb.append(key); + sb.append("/>"); + +// Emit a new tag <k> + + } else { + sb.append(toString(value, key)); + } + } + if (tagName != null) { + +// Emit the </tagname> close tag + + sb.append("</"); + sb.append(tagName); + sb.append('>'); + } + return sb.toString(); + +// XML does not have good support for arrays. If an array appears in a place +// where XML is lacking, synthesize an <array> element. + + } else { + if (object.getClass().isArray()) { + object = new JSONArray(object); + } + if (object instanceof JSONArray) { + ja = (JSONArray)object; + length = ja.length(); + for (i = 0; i < length; i += 1) { + sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName)); + } + return sb.toString(); + } else { + string = (object == null) ? "null" : escape(object.toString()); + return (tagName == null) ? "\"" + string + "\"" : + (string.length() == 0) ? "<" + tagName + "/>" : + "<" + tagName + ">" + string + "</" + tagName + ">"; + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XMLTokener.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XMLTokener.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XMLTokener.java new file mode 100644 index 0000000..aa18c85 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/json/XMLTokener.java @@ -0,0 +1,360 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.json; + +/** + * The XMLTokener extends the JSONTokener to provide additional methods + * for the parsing of XML texts. + * @author JSON.org + * @version 2010-12-24 + */ +public class XMLTokener extends JSONTokener { + + + /** The table of entity values. It initially contains Character values for + * amp, apos, gt, lt, quot. + */ + public static final java.util.HashMap entity; + + static { + entity = new java.util.HashMap(8); + entity.put("amp", XML.AMP); + entity.put("apos", XML.APOS); + entity.put("gt", XML.GT); + entity.put("lt", XML.LT); + entity.put("quot", XML.QUOT); + } + + /** + * Construct an XMLTokener from a string. + * @param s A source string. + */ + public XMLTokener(String s) { + super(s); + } + + /** + * Get the text in the CDATA block. + * @return The string up to the <code>]]></code>. + * @throws JSONException If the <code>]]></code> is not found. + */ + public String nextCDATA() throws JSONException { + char c; + int i; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + if (end()) { + throw syntaxError("Unclosed CDATA"); + } + sb.append(c); + i = sb.length() - 3; + if (i >= 0 && sb.charAt(i) == ']' && + sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') { + sb.setLength(i); + return sb.toString(); + } + } + } + + + /** + * Get the next XML outer token, trimming whitespace. There are two kinds + * of tokens: the '<' character which begins a markup tag, and the content + * text between markup tags. + * + * @return A string, or a '<' Character, or null if there is no more + * source text. + * @throws JSONException + */ + public Object nextContent() throws JSONException { + char c; + StringBuffer sb; + do { + c = next(); + } while (Character.isWhitespace(c)); + if (c == 0) { + return null; + } + if (c == '<') { + return XML.LT; + } + sb = new StringBuffer(); + for (;;) { + if (c == '<' || c == 0) { + back(); + return sb.toString().trim(); + } + if (c == '&') { + sb.append(nextEntity(c)); + } else { + sb.append(c); + } + c = next(); + } + } + + + /** + * Return the next entity. These entities are translated to Characters: + * <code>& ' > < "</code>. + * @param ampersand An ampersand character. + * @return A Character or an entity String if the entity is not recognized. + * @throws JSONException If missing ';' in XML entity. + */ + public Object nextEntity(char ampersand) throws JSONException { + StringBuffer sb = new StringBuffer(); + for (;;) { + char c = next(); + if (Character.isLetterOrDigit(c) || c == '#') { + sb.append(Character.toLowerCase(c)); + } else if (c == ';') { + break; + } else { + throw syntaxError("Missing ';' in XML entity: &" + sb); + } + } + String string = sb.toString(); + Object object = entity.get(string); + return object != null ? object : ampersand + string + ";"; + } + + + /** + * Returns the next XML meta token. This is used for skipping over <!...> + * and <?...?> structures. + * @return Syntax characters (<code>< > / = ! ?</code>) are returned as + * Character, and strings and names are returned as Boolean. We don't care + * what the values actually are. + * @throws JSONException If a string is not properly closed or if the XML + * is badly structured. + */ + public Object nextMeta() throws JSONException { + char c; + char q; + do { + c = next(); + } while (Character.isWhitespace(c)); + switch (c) { + case 0: + throw syntaxError("Misshaped meta tag"); + case '<': + return XML.LT; + case '>': + return XML.GT; + case '/': + return XML.SLASH; + case '=': + return XML.EQ; + case '!': + return XML.BANG; + case '?': + return XML.QUEST; + case '"': + case '\'': + q = c; + for (;;) { + c = next(); + if (c == 0) { + throw syntaxError("Unterminated string"); + } + if (c == q) { + return Boolean.TRUE; + } + } + default: + for (;;) { + c = next(); + if (Character.isWhitespace(c)) { + return Boolean.TRUE; + } + switch (c) { + case 0: + case '<': + case '>': + case '/': + case '=': + case '!': + case '?': + case '"': + case '\'': + back(); + return Boolean.TRUE; + } + } + } + } + + + /** + * Get the next XML Token. These tokens are found inside of angle + * brackets. It may be one of these characters: <code>/ > = ! ?</code> or it + * may be a string wrapped in single quotes or double quotes, or it may be a + * name. + * @return a String or a Character. + * @throws JSONException If the XML is not well formed. + */ + public Object nextToken() throws JSONException { + char c; + char q; + StringBuffer sb; + do { + c = next(); + } while (Character.isWhitespace(c)); + switch (c) { + case 0: + throw syntaxError("Misshaped element"); + case '<': + throw syntaxError("Misplaced '<'"); + case '>': + return XML.GT; + case '/': + return XML.SLASH; + case '=': + return XML.EQ; + case '!': + return XML.BANG; + case '?': + return XML.QUEST; + +// Quoted string + + case '"': + case '\'': + q = c; + sb = new StringBuffer(); + for (;;) { + c = next(); + if (c == 0) { + throw syntaxError("Unterminated string"); + } + if (c == q) { + return sb.toString(); + } + if (c == '&') { + sb.append(nextEntity(c)); + } else { + sb.append(c); + } + } + default: + +// Name + + sb = new StringBuffer(); + for (;;) { + sb.append(c); + c = next(); + if (Character.isWhitespace(c)) { + return sb.toString(); + } + switch (c) { + case 0: + return sb.toString(); + case '>': + case '/': + case '=': + case '!': + case '?': + case '[': + case ']': + back(); + return sb.toString(); + case '<': + case '"': + case '\'': + throw syntaxError("Bad character in a name"); + } + } + } + } + + + /** + * Skip characters until past the requested string. + * If it is not found, we are left at the end of the source with a result of false. + * @param to A string to skip past. + * @throws JSONException + */ + public boolean skipPast(String to) throws JSONException { + boolean b; + char c; + int i; + int j; + int offset = 0; + int length = to.length(); + char[] circle = new char[length]; + + /* + * First fill the circle buffer with as many characters as are in the + * to string. If we reach an early end, bail. + */ + + for (i = 0; i < length; i += 1) { + c = next(); + if (c == 0) { + return false; + } + circle[i] = c; + } + /* + * We will loop, possibly for all of the remaining characters. + */ + for (;;) { + j = offset; + b = true; + /* + * Compare the circle buffer with the to string. + */ + for (i = 0; i < length; i += 1) { + if (circle[j] != to.charAt(i)) { + b = false; + break; + } + j += 1; + if (j >= length) { + j -= length; + } + } + /* + * If we exit the loop with b intact, then victory is ours. + */ + if (b) { + return true; + } + /* + * Get the next character. If there isn't one, then defeat is ours. + */ + c = next(); + if (c == 0) { + return false; + } + /* + * Shove the character in the circle buffer and advance the + * circle offset. The offset is mod n. + */ + circle[offset] = c; + offset += 1; + if (offset >= length) { + offset -= length; + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/LogWriter.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/LogWriter.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/LogWriter.java new file mode 100644 index 0000000..662c561 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/LogWriter.java @@ -0,0 +1,265 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.log; + +/** + * interface LogWriter + * + * LogWriter interface for Pulse Logging. + * + * @since GemFire 7.0.1 + * + */ +public interface LogWriter { + /** + * Returns true if "severe" log messages are enabled. Returns false if + * "severe" log messages are disabled. + */ + public boolean severeEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "severe". + */ + public void severe(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "severe". + */ + public void severe(String msg); + + /** + * Writes an exception to this writer. The exception level is "severe". + */ + public void severe(Throwable ex); + + /** + * Returns true if "error" log messages are enabled. Returns false if "error" + * log messages are disabled. + */ + // public boolean errorEnabled(); + /** + * Writes both a message and exception to this writer. The message level is + * "error". + */ + // public void error(String msg, Throwable ex); + /** + * Writes a message to this writer. The message level is "error". + */ + // public void error(String msg); + /** + * Writes an exception to this writer. The exception level is "error". + */ + // public void error(Throwable ex); + /** + * Returns true if "warning" log messages are enabled. Returns false if + * "warning" log messages are disabled. + */ + public boolean warningEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "warning". + */ + public void warning(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "warning". + */ + public void warning(String msg); + + /** + * Writes an exception to this writer. The exception level is "warning". + */ + public void warning(Throwable ex); + + /** + * Returns true if "info" log messages are enabled. Returns false if "info" + * log messages are disabled. + */ + public boolean infoEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "information". + */ + public void info(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "information". + */ + public void info(String msg); + + /** + * Writes an exception to this writer. The exception level is "information". + */ + public void info(Throwable ex); + + /** + * Returns true if "config" log messages are enabled. Returns false if + * "config" log messages are disabled. + */ + public boolean configEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "config". + */ + public void config(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "config". + */ + public void config(String msg); + + /** + * Writes an exception to this writer. The exception level is "config". + */ + public void config(Throwable ex); + + /** + * Returns true if "fine" log messages are enabled. Returns false if "fine" + * log messages are disabled. + */ + public boolean fineEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "fine". + */ + public void fine(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "fine". + */ + public void fine(String msg); + + /** + * Writes an exception to this writer. The exception level is "fine". + */ + public void fine(Throwable ex); + + /** + * Returns true if "finer" log messages are enabled. Returns false if "finer" + * log messages are disabled. + */ + public boolean finerEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "finer". + */ + public void finer(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "finer". + */ + public void finer(String msg); + + /** + * Writes an exception to this writer. The exception level is "finer". + */ + public void finer(Throwable ex); + + /** + * Log a method entry. + * <p> + * The logging is done using the <code>finer</code> level. The string message + * will start with <code>"ENTRY"</code> and include the class and method + * names. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of the method that issued the logging request. + */ + public void entering(String sourceClass, String sourceMethod); + + /** + * Log a method return. + * <p> + * The logging is done using the <code>finer</code> level. The string message + * will start with <code>"RETURN"</code> and include the class and method + * names. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of the method that issued the logging request. + */ + public void exiting(String sourceClass, String sourceMethod); + + /** + * Log throwing an exception. + * <p> + * Use to log that a method is terminating by throwing an exception. The + * logging is done using the <code>finer</code> level. + * <p> + * This is a convenience method that could be done instead by calling + * {@link #finer(String, Throwable)}. The string message will start with + * <code>"THROW"</code> and include the class and method names. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of the method that issued the logging request. + * @param thrown + * The Throwable that is being thrown. + */ + public void throwing(String sourceClass, String sourceMethod, Throwable thrown); + + /** + * Returns true if "finest" log messages are enabled. Returns false if + * "finest" log messages are disabled. + */ + public boolean finestEnabled(); + + /** + * Writes both a message and exception to this writer. The message level is + * "finest". + */ + public void finest(String msg, Throwable ex); + + /** + * Writes a message to this writer. The message level is "finest". + */ + public void finest(String msg); + + /** + * Writes an exception to this writer. The exception level is "finest". + */ + public void finest(Throwable ex); + + /** + * Returns a 1.4 logging handler that can be used to direct application output + * to this GemFire logger using the standard JDK logger APIs. Each time this + * method is called it creates a new instance of a Handler so care should be + * taken to not call this method too often. + */ + // public Handler getHandler(); + + /** + * A mechanism for accessing the abstraction layer used for + * internationalization. + * + * @return LogWriterI18n + */ + // public LogWriterI18n convertToLogWriterI18n(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/MessageFormatter.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/MessageFormatter.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/MessageFormatter.java new file mode 100644 index 0000000..11fe290 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/MessageFormatter.java @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.log; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +import com.vmware.geode.tools.pulse.internal.data.PulseConstants; +import com.vmware.geode.tools.pulse.internal.data.Repository; + +/** + * Class MessageFormatter + * + * MessageFormatter is the custom formatter class for formatting the log + * messages. + * + * @since GemFire version 7.0.1 + */ +public class MessageFormatter extends Formatter { + + public MessageFormatter() { + super(); + } + + @Override + public String format(LogRecord record) { + DateFormat df = new SimpleDateFormat(Repository.get().getPulseConfig() + .getLogDatePattern()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + pw.println(); + pw.print("["); + pw.print(record.getLevel().getName()); + pw.print(" "); + pw.print(df.format(new Date(record.getMillis()))); + String threadName = Thread.currentThread().getName(); + if (threadName != null) { + pw.print(" "); + pw.print(threadName); + } + pw.print(" tid=0x"); + pw.print(Long.toHexString(Thread.currentThread().getId())); + pw.print("] "); + pw.print("(msgTID="); + pw.print(record.getThreadID()); + + pw.print(" msgSN="); + pw.print(record.getSequenceNumber()); + + pw.print(") "); + + pw.println("[" + PulseConstants.APP_NAME + "]"); + + pw.println("[" + record.getLoggerName() + "]"); + + pw.println(record.getMessage()); + + if (record.getThrown() != null) { + record.getThrown().printStackTrace(pw); + } + pw.close(); + try { + sw.close(); + } catch (IOException ignore) { + } + String result = sw.toString(); + return result; + } + + public String getHead(Handler h) { + return super.getHead(h); + } + + public String getTail(Handler h) { + return super.getTail(h); + } +} // End of Class MessageFormatter http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogWriter.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogWriter.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogWriter.java new file mode 100644 index 0000000..fe4e88b --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogWriter.java @@ -0,0 +1,299 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.log; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.FileHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vmware.geode.tools.pulse.internal.data.PulseConfig; +import com.vmware.geode.tools.pulse.internal.data.Repository; + +/** + * Class PulseLogWriter + * + * PulseLogWriter is the implementation of LogWriter. + * + * @since GemFire 7.0.1 + * + */ +public class PulseLogWriter implements LogWriter { + + // Log File handle Object + private FileHandler fileHandler; + + // Message Formatter Object + private static MessageFormatter messageformatter; + + // pulse log writer + private static PulseLogWriter pulseLogger = null; + + // Logger Object + private Logger logger; + + private PulseLogWriter() { + PulseConfig pulseConfig = Repository.get().getPulseConfig(); + // Create Logger + logger = Logger.getLogger(this.getClass().getName()); + + // Set minimum log level to level passed + logger.setLevel(pulseConfig.getLogLevel()); + + try { + // Get file handler to log messages into log file. + if (fileHandler == null) { + fileHandler = new FileHandler( + pulseConfig.getLogFileFullName(), + pulseConfig.getLogFileSize(), + pulseConfig.getLogFileCount(), + pulseConfig.getLogAppend()); + + // Log Message Formatter + messageformatter = new MessageFormatter(); + fileHandler.setFormatter(messageformatter); + } + + // Add File Handler to logger object + logger.addHandler(fileHandler); + } catch (SecurityException e) { + logger.setUseParentHandlers(true); + e.printStackTrace(); + } catch (IOException e) { + logger.setUseParentHandlers(true); + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * @param jsonErr + * @param errorData + */ + public void logJSONError(Exception jsonErr, Object errorData) { + + // print details of thrown exception and data that couldn't be converted to + // json + if (this.fineEnabled()) { + + // write errors + StringWriter swBuffer = new StringWriter(); + PrintWriter prtWriter = new PrintWriter(swBuffer); + jsonErr.printStackTrace(prtWriter); + this.fine("JSON Error Details : " + swBuffer.toString() + "\n"); + + this.fine("Erroneous Data : " + + ((errorData != null) ? errorData.toString() + : "Not Available for output") + "\n"); + } + } + + public static synchronized PulseLogWriter getLogger() { + if (null == pulseLogger) { + pulseLogger = new PulseLogWriter(); + } + return pulseLogger; + } + + @Override + public boolean severeEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + @Override + public void severe(String msg, Throwable ex) { + logger.logp(Level.SEVERE, "", "", msg, ex); + } + + @Override + public void severe(String msg) { + logger.severe(msg); + } + + @Override + public void severe(Throwable ex) { + logger.logp(Level.SEVERE, "", "", "", ex); + } + + /* + @Override + public boolean errorEnabled() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void error(String msg, Throwable ex) { + // TODO Auto-generated method stub + + } + + @Override + public void error(String msg) { + // TODO Auto-generated method stub + + } + + @Override + public void error(Throwable ex) { + // TODO Auto-generated method stub + + } + */ + + @Override + public boolean warningEnabled() { + return logger.isLoggable(Level.WARNING); + } + + @Override + public void warning(String msg, Throwable ex) { + logger.logp(Level.WARNING, "", "", msg, ex); + } + + @Override + public void warning(String msg) { + logger.warning(msg); + } + + @Override + public void warning(Throwable ex) { + logger.logp(Level.WARNING, "", "", "", ex); + } + + @Override + public boolean infoEnabled() { + return logger.isLoggable(Level.INFO); + } + + @Override + public void info(String msg, Throwable ex) { + logger.logp(Level.INFO, "", "", msg, ex); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(Throwable ex) { + logger.logp(Level.WARNING, "", "", "", ex); + } + + @Override + public boolean configEnabled() { + return logger.isLoggable(Level.CONFIG); + } + + @Override + public void config(String msg, Throwable ex) { + logger.logp(Level.CONFIG, "", "", msg, ex); + } + + @Override + public void config(String msg) { + logger.config(msg); + } + + @Override + public void config(Throwable ex) { + logger.logp(Level.CONFIG, "", "", "", ex); + } + + @Override + public boolean fineEnabled() { + return logger.isLoggable(Level.FINE); + } + + @Override + public void fine(String msg, Throwable ex) { + logger.logp(Level.FINE, "", "", msg, ex); + } + + @Override + public void fine(String msg) { + logger.fine(msg); + } + + @Override + public void fine(Throwable ex) { + logger.logp(Level.FINE, "", "", "", ex); + } + + @Override + public boolean finerEnabled() { + return logger.isLoggable(Level.FINER); + } + + @Override + public void finer(String msg, Throwable ex) { + logger.logp(Level.FINER, "", "", msg, ex); + } + + @Override + public void finer(String msg) { + logger.finer(msg); + } + + @Override + public void finer(Throwable ex) { + logger.logp(Level.FINER, "", "", "", ex); + } + + @Override + public void entering(String sourceClass, String sourceMethod) { + logger.entering(sourceClass, sourceMethod); + } + + @Override + public void exiting(String sourceClass, String sourceMethod) { + logger.exiting(sourceClass, sourceMethod); + } + + @Override + public void throwing(String sourceClass, String sourceMethod, Throwable thrown) { + logger.throwing(sourceClass, sourceMethod, thrown); + } + + @Override + public boolean finestEnabled() { + return logger.isLoggable(Level.FINEST); + } + + @Override + public void finest(String msg, Throwable ex) { + logger.logp(Level.FINEST, "", "", msg, ex); + } + + @Override + public void finest(String msg) { + logger.finest(msg); + } + + @Override + public void finest(Throwable ex) { + logger.logp(Level.FINEST, "", "", "", ex); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogger.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogger.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogger.java new file mode 100644 index 0000000..2de81bd --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/log/PulseLogger.java @@ -0,0 +1,142 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.log; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import com.vmware.geode.tools.pulse.internal.data.PulseConstants; + +/** + * Class PulseLogger + * + * PulseLogger is the custom logger class for Pulse Web Application. It logs + * messages to the file in custom format. + * + * @since GemFire version 7.0.Beta + */ +public class PulseLogger { + + // Pulse Application Log File + private static final String LOG_FILE_NAME = PulseConstants.PULSE_LOG_FILE_LOCATION + + "/" + PulseConstants.PULSE_LOG_FILE; + + // Date pattern to be used in log messages + public static final String LOG_MESSAGE_DATE_PATTERN = "dd/MM/yyyy hh:mm:ss.SSS"; + + // The log file size is set to 1MB. + public static final int FILE_SIZE = 1024 * 1024; + + // The log file count set to 1 files. + public static final int FILE_COUNT = 5; + + // Append logs is set to true. + public static final boolean FLAG_APPEND = true; + + // Log File handle Object + private static FileHandler fileHandler; + + // Message Formatter Object + private static MessageFormatter messageformatter; + + // Logger Object + private static Logger logger; + + public static Logger getLogger(String name) { + // Create Logger + logger = Logger.getLogger(name); + + // Set minimum log level to inform + logger.setLevel(Level.INFO); + + // Get file handler to log messages into log file. + try { + // fileHandler = new FileHandler(LOG_FILE_NAME, FILE_SIZE, FILE_COUNT, + // FLAG_APPEND); + fileHandler = new FileHandler(LOG_FILE_NAME, FLAG_APPEND); + + // Log Message Formatter + messageformatter = new MessageFormatter(); + + fileHandler.setFormatter(messageformatter); + + // Add File Handler to logger object + logger.addHandler(fileHandler); + + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + return logger; + } + + /** + * Class MessageFormatter + * + * MessageFormatter is the custom formatter class for formatting the log + * messages. + * + * @since GemFire version 7.0.Beta 2012-09-23 + */ + private static class MessageFormatter extends Formatter { + + public MessageFormatter() { + super(); + } + + @Override + public String format(LogRecord record) { + // Set DateFormat + DateFormat df = new SimpleDateFormat(PulseLogger.LOG_MESSAGE_DATE_PATTERN); + StringBuilder builder = new StringBuilder(1000); + + // Format Log Message + builder.append(df.format(new Date(record.getMillis()))).append(" - "); + builder.append("[ " + PulseConstants.APP_NAME + " ] - "); + builder.append("[").append(record.getSourceClassName()).append("."); + builder.append(record.getSourceMethodName()).append("] - "); + builder.append("[").append(record.getLevel()).append("] - "); + builder.append(formatMessage(record)); + builder.append(System.getProperty("line.separator")); + + return builder.toString(); + } + + public String getHead(Handler h) { + return super.getHead(h); + } + + public String getTail(Handler h) { + return super.getTail(h); + } + } // End of Class MessageFormatter +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthentication.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthentication.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthentication.java new file mode 100644 index 0000000..6506667 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthentication.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.security; + +import java.util.ArrayList; +import java.util.Collection; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; + +import com.vmware.geode.tools.pulse.internal.data.PulseConstants; +import com.vmware.geode.tools.pulse.internal.log.PulseLogWriter; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * Spring security authentication object for GemFire + * <p> + * To use GemFire Integrated Security Model set Spring Application Profile to pulse.authentication.gemfire + * <p> + * 1. Authentication : + * 1.a GemFire profile creates JMX connection with given credentials at the login time. + * 1.b Successful connect is considered as Successful Authentication for Pulse WebApp + * <p> + * <p> + * 2. Authorization : + * 2.a Using newly created authenticated connection AccessControlMXBean is called to get authentication + * levels. See @See {@link #populateAuthorities(JMXConnector)}. This sets Spring Security Authorities + * 2.b DataBrowser end-points are required to be authorized against Spring Granted Authority + * @since GemFire version 9.0 + */ +public class GemFireAuthentication extends UsernamePasswordAuthenticationToken { + + private final static PulseLogWriter logger = PulseLogWriter.getLogger(); + + private JMXConnector jmxc = null; + + public GemFireAuthentication(Object principal, Object credentials, Collection<GrantedAuthority> list, JMXConnector jmxc) { + super(principal, credentials, list); + this.jmxc = jmxc; + } + + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + public static ArrayList<GrantedAuthority> populateAuthorities(JMXConnector jmxc) { + ObjectName name; + ArrayList<GrantedAuthority> authorities = new ArrayList<>(); + try { + name = new ObjectName(PulseConstants.OBJECT_NAME_ACCESSCONTROL_MBEAN); + MBeanServerConnection mbeanServer = jmxc.getMBeanServerConnection(); + + for (String role : PulseConstants.PULSE_ROLES) { + Object[] params = role.split(":"); + String[] signature = new String[] { String.class.getCanonicalName(), String.class.getCanonicalName() }; + boolean result = (Boolean) mbeanServer.invoke(name, "authorize", params, signature); + if (result) { + authorities.add(new SimpleGrantedAuthority(role)); + } + } + } + catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + + return authorities; + + } + + public JMXConnector getJmxc() { + return jmxc; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java new file mode 100644 index 0000000..cd37aea --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.security; + +import java.util.Collection; +import javax.management.remote.JMXConnector; + +import com.vmware.geode.tools.pulse.internal.data.Repository; +import com.vmware.geode.tools.pulse.internal.log.PulseLogWriter; + +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; + +/** + * Spring security AuthenticationProvider for GemFire. It connects to gemfire manager using given credentials. + * Successful connect is treated as successful authentication and web user is authenticated + * @since GemFire version 9.0 + */ +public class GemFireAuthenticationProvider implements AuthenticationProvider { + + private final static PulseLogWriter LOGGER = PulseLogWriter.getLogger(); + + public GemFireAuthenticationProvider() { + System.out.println("here"); + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + if (authentication instanceof GemFireAuthentication) { + GemFireAuthentication gemAuth = (GemFireAuthentication) authentication; + LOGGER.fine("GemAuthentication is connected? = " + gemAuth.getJmxc()); + if (gemAuth.getJmxc() != null && gemAuth.isAuthenticated()) return gemAuth; + } + + String name = authentication.getName(); + String password = authentication.getCredentials().toString(); + + try { + LOGGER.fine("Connecting to GemFire with user=" + name); + JMXConnector jmxc = Repository.get().getCluster(name, password).connectToGemFire(); + if (jmxc != null) { + Collection<GrantedAuthority> list = GemFireAuthentication.populateAuthorities(jmxc); + GemFireAuthentication auth = new GemFireAuthentication(authentication.getPrincipal(), + authentication.getCredentials(), list, jmxc); + LOGGER.fine("For user " + name + " authList=" + list); + return auth; + } else { + throw new AuthenticationServiceException("JMX Connection unavailable"); + } + } catch (Exception e) { + throw new BadCredentialsException("Error connecting to GemFire JMX Server", e); + } + } + + @Override + public boolean supports(Class<?> authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/LogoutHandler.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/LogoutHandler.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/LogoutHandler.java new file mode 100644 index 0000000..5056ce2 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/LogoutHandler.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.security; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.vmware.geode.tools.pulse.internal.data.Repository; +import com.vmware.geode.tools.pulse.internal.log.PulseLogWriter; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; + +/** + * Handler is used to close jmx connection maintained at user-level + * + */ +public class LogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler { + + public LogoutHandler(String defaultTargetURL) { + this.setDefaultTargetUrl(defaultTargetURL); + } + + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + PulseLogWriter LOGGER = PulseLogWriter.getLogger(); + LOGGER.fine("Invoked #LogoutHandler ..."); + if (Repository.get().isUseGemFireCredentials()) { + GemFireAuthentication gemauthentication = (GemFireAuthentication) authentication; + if(gemauthentication!=null) { + gemauthentication.getJmxc().close(); + LOGGER.info("#LogoutHandler : Closing GemFireAuthentication JMX Connection..."); + } + } + super.onLogoutSuccess(request, response, authentication); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDetailsService.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDetailsService.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDetailsService.java new file mode 100644 index 0000000..8b5e409 --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDetailsService.java @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.vmware.geode.tools.pulse.internal.data.Cluster; +import com.vmware.geode.tools.pulse.internal.data.PulseConstants; +import com.vmware.geode.tools.pulse.internal.data.Repository; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.text.DecimalFormat; + +/** + * Class ClusterDetailsService + * + * This service class has implementation for providing cluster's basic + * statistical data. + * + * @since GemFire version 7.5 + */ + +@Component +@Service("ClusterDetails") +@Scope("singleton") +public class ClusterDetailsService implements PulseService { + + private final ObjectMapper mapper = new ObjectMapper(); + + public ObjectNode execute(final HttpServletRequest request) throws Exception { + + String userName = request.getUserPrincipal().getName(); + + // get cluster object + Cluster cluster = Repository.get().getCluster(); + + // json object to be sent as response + ObjectNode responseJSON = mapper.createObjectNode(); + + Cluster.Alert[] alertsList = cluster.getAlertsList(); + int severeAlertCount = 0; + int errorAlertCount = 0; + int warningAlertCount = 0; + int infoAlertCount = 0; + + for (Cluster.Alert alertObj : alertsList) { + if (alertObj.getSeverity() == Cluster.Alert.SEVERE) { + severeAlertCount++; + } else if (alertObj.getSeverity() == Cluster.Alert.ERROR) { + errorAlertCount++; + } else if (alertObj.getSeverity() == Cluster.Alert.WARNING) { + warningAlertCount++; + } else { + infoAlertCount++; + } + } + // getting basic details of Cluster + responseJSON.put("clusterName", cluster.getServerName()); + responseJSON.put("severeAlertCount", severeAlertCount); + responseJSON.put("errorAlertCount", errorAlertCount); + responseJSON.put("warningAlertCount", warningAlertCount); + responseJSON.put("infoAlertCount", infoAlertCount); + + responseJSON.put("totalMembers", cluster.getMemberCount()); + responseJSON.put("servers", cluster.getServerCount()); + responseJSON.put("clients", cluster.getClientConnectionCount()); + responseJSON.put("locators", cluster.getLocatorCount()); + responseJSON.put("totalRegions", cluster.getTotalRegionCount()); + Long heapSize = cluster.getTotalHeapSize(); + + DecimalFormat df2 = new DecimalFormat( + PulseConstants.DECIMAL_FORMAT_PATTERN); + Double heapS = heapSize.doubleValue() / 1024; + responseJSON.put("totalHeap", Double.valueOf(df2.format(heapS))); + responseJSON.put("functions", cluster.getRunningFunctionCount()); + responseJSON.put("uniqueCQs", cluster.getRegisteredCQCount()); + responseJSON.put("subscriptions", cluster.getSubscriptionCount()); + responseJSON.put("txnCommitted", cluster.getTxnCommittedCount()); + responseJSON.put("txnRollback", cluster.getTxnRollbackCount()); + responseJSON.put("userName", userName); + + return responseJSON; + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDiskThroughputService.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDiskThroughputService.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDiskThroughputService.java new file mode 100644 index 0000000..11247bc --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterDiskThroughputService.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.vmware.geode.tools.pulse.internal.data.Cluster; +import com.vmware.geode.tools.pulse.internal.data.Repository; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/** + * Class ClusterDiskThroughput This class contains implementations for getting + * cluster's current disk throughput details and its trend over time + * + * @since GemFire version 7.0.Beta + */ + +@Component +@Service("ClusterDiskThroughput") +@Scope("singleton") +public class ClusterDiskThroughputService implements PulseService { + + private final ObjectMapper mapper = new ObjectMapper(); + + public ObjectNode execute(final HttpServletRequest request) throws Exception { + + // get cluster object + Cluster cluster = Repository.get().getCluster(); + + // json object to be sent as response + ObjectNode responseJSON = mapper.createObjectNode(); + + // cluster's Throughout Writes trend added to json response object + // CircularFifoBuffer throughoutWritesTrend = + // cluster.getThroughoutWritesTrend(); + double currentThroughputWrites = cluster.getDiskWritesRate(); + double currentThroughputReads = cluster.getDiskReadsRate(); + + responseJSON.put("currentThroughputReads", currentThroughputReads); + responseJSON.put("throughputReads", + mapper.valueToTree(cluster.getStatisticTrend(Cluster.CLUSTER_STAT_THROUGHPUT_READS))); + + responseJSON.put("currentThroughputWrites", currentThroughputWrites); + responseJSON.put("throughputWrites", + mapper.valueToTree( cluster.getStatisticTrend(Cluster.CLUSTER_STAT_THROUGHPUT_WRITES))); + + // Send json response + return responseJSON; + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterGCPausesService.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterGCPausesService.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterGCPausesService.java new file mode 100644 index 0000000..52ba79c --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterGCPausesService.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.vmware.geode.tools.pulse.internal.data.Cluster; +import com.vmware.geode.tools.pulse.internal.data.Repository; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/** + * Class ClusterGCPausesService + * + * This class contains implementations of getting Cluster's GC Pauses (JVM + * Pauses) Details and its trend over the time. + * + * @since GemFire version 7.5 + */ + +@Component +@Service("ClusterJVMPauses") +@Scope("singleton") +public class ClusterGCPausesService implements PulseService { + + private final ObjectMapper mapper = new ObjectMapper(); + + public ObjectNode execute(final HttpServletRequest request) throws Exception { + + // get cluster object + Cluster cluster = Repository.get().getCluster(); + + // json object to be sent as response + ObjectNode responseJSON = mapper.createObjectNode(); + // cluster's GC Pauses trend added to json response object + + ArrayNode pauses = mapper.createArrayNode(); + for (Object obj : cluster.getStatisticTrend(Cluster.CLUSTER_STAT_GARBAGE_COLLECTION)) { + if (obj instanceof Number) { + pauses.add(((Number) obj).longValue()); + } + } + responseJSON.put("currentGCPauses", cluster.getGarbageCollectionCount()); + responseJSON.put("gCPausesTrend", pauses); + // Send json response + return responseJSON; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9a2b5d7b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterKeyStatisticsService.java ---------------------------------------------------------------------- diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterKeyStatisticsService.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterKeyStatisticsService.java new file mode 100644 index 0000000..8c15ccc --- /dev/null +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/service/ClusterKeyStatisticsService.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.vmware.geode.tools.pulse.internal.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.vmware.geode.tools.pulse.internal.data.Cluster; +import com.vmware.geode.tools.pulse.internal.data.Repository; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/** + * Class ClusterKeyStatisticsService + * + * This class contains implementations of getting Cluster's current Reads, + * Writes and queries details and their trends over the time. + * + * @since GemFire version 7.5 + */ + +@Component +@Service("ClusterKeyStatistics") +@Scope("singleton") +public class ClusterKeyStatisticsService implements PulseService { + + private final ObjectMapper mapper = new ObjectMapper(); + + public ObjectNode execute(final HttpServletRequest request) throws Exception { + + // get cluster object + Cluster cluster = Repository.get().getCluster(); + + // json object to be sent as response + ObjectNode responseJSON = mapper.createObjectNode(); + + responseJSON.put("writePerSecTrend", + mapper.valueToTree(cluster.getStatisticTrend(Cluster.CLUSTER_STAT_WRITES_PER_SECOND))); + + responseJSON.put("readPerSecTrend", + mapper.valueToTree(cluster.getStatisticTrend(Cluster.CLUSTER_STAT_READ_PER_SECOND))); + + responseJSON.put("queriesPerSecTrend", + mapper.valueToTree(cluster.getStatisticTrend(Cluster.CLUSTER_STAT_QUERIES_PER_SECOND))); + + // Send json response + return responseJSON; + + } +}