You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by hi...@apache.org on 2016/09/09 21:44:01 UTC
[64/83] [abbrv] incubator-geode git commit: GEODE-37 renamed pulse
package
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eff7f216/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
+douglas@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/eff7f216/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..2aa8b8d
--- /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 org.apache.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/eff7f216/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..7995af0
--- /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 org.apache.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/eff7f216/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..72c9831
--- /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 org.apache.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/eff7f216/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..3828759
--- /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 org.apache.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 org.apache.geode.tools.pulse.internal.data.PulseConstants;
+import org.apache.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/eff7f216/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..901f2f4
--- /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 org.apache.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 org.apache.geode.tools.pulse.internal.data.PulseConfig;
+import org.apache.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/eff7f216/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..b4f08c1
--- /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 org.apache.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 org.apache.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/eff7f216/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..425f5a5
--- /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 org.apache.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 org.apache.geode.tools.pulse.internal.data.PulseConstants;
+import org.apache.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/eff7f216/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..f4575cc
--- /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 org.apache.geode.tools.pulse.internal.security;
+
+import java.util.Collection;
+import javax.management.remote.JMXConnector;
+
+import org.apache.geode.tools.pulse.internal.data.Repository;
+import org.apache.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/eff7f216/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..451e19a
--- /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 org.apache.geode.tools.pulse.internal.security;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.geode.tools.pulse.internal.data.Repository;
+import org.apache.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/eff7f216/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..0216cb0
--- /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 org.apache.geode.tools.pulse.internal.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.geode.tools.pulse.internal.data.Cluster;
+import org.apache.geode.tools.pulse.internal.data.PulseConstants;
+import org.apache.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/eff7f216/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..b50b551
--- /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 org.apache.geode.tools.pulse.internal.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.geode.tools.pulse.internal.data.Cluster;
+import org.apache.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/eff7f216/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..164e7e3
--- /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 org.apache.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 org.apache.geode.tools.pulse.internal.data.Cluster;
+import org.apache.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/eff7f216/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..9c35a3c
--- /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 org.apache.geode.tools.pulse.internal.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.geode.tools.pulse.internal.data.Cluster;
+import org.apache.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;
+
+ }
+}