Skip to content

Commit

Permalink
Issue #109: Refact History object into a generic.
Browse files Browse the repository at this point in the history
  • Loading branch information
mk23 committed Apr 15, 2016
1 parent 28a0788 commit 50e3c0f
Show file tree
Hide file tree
Showing 8 changed files with 495 additions and 420 deletions.
90 changes: 0 additions & 90 deletions src/main/java/com/github/mk23/jmxproxy/core/History.java

This file was deleted.

9 changes: 5 additions & 4 deletions src/main/java/com/github/mk23/jmxproxy/core/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,18 @@ public final void removeMBean(final String mbeanName) {
/**
* <p>Sets the thread local history request limit.</p>
*
* Set the thread local limit for all {@link History} when serializing to JSON.
* Because this method returns its object, requesting serialization can be done
* with a single statement.
* Set the thread local limit for all {@link com.github.mk23.jmxproxy.util.History} when
* serializing to JSON. Because this method returns its object, requesting serialization
* can be done * with a single statement.
*
* <p>For example:</p>
*
* <code>return Response.ok(host.setLimit(5)).build();</code>
*
* @see <a href="http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Response.html">javax.ws.rs.core.Response</a>
*
* @param bound the number of items to retreive from {@link History} for this thread.
* @param bound the number of items to retreive from {@link com.github.mk23.jmxproxy.util.History}
* for this thread.
*
* @return this host object for chaining calls.
*/
Expand Down
53 changes: 29 additions & 24 deletions src/main/java/com/github/mk23/jmxproxy/core/MBean.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.mk23.jmxproxy.core;

import com.github.mk23.jmxproxy.util.History;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializable;
Expand All @@ -8,7 +10,9 @@

import java.io.IOException;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand All @@ -26,7 +30,7 @@
* @version 3.2.0
*/
public class MBean implements JsonSerializable {
private final Map<String, History> attributes;
private final Map<String, History<Attribute>> attributes;
private final ThreadLocal<Integer> limit = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
Expand All @@ -40,27 +44,30 @@ protected Integer initialValue() {
* Creates a map of {@link Attribute} name to {@link History} of associated values.
*/
public MBean() {
attributes = new HashMap<String, History>();
attributes = new HashMap<String, History<Attribute>>();
}

/**
* <p>Inserts a new Attribute name to History association.</p>
*
* Creates a new {@link Attribute} value {@link History} object and inserts into
* the map store associating it to the specified attribute name.
* Optionally creates a new {@link History} object and inserts into the map store associating
* it to the specified attribute name. Appends the specified {@link Attribute} value to the
* {@link History}.
*
* @param attributeName name of the {@link Attribute} used as the map key.
* @param size number of items to preserve in the associated {@link History}.
*
* @return the newly created empty {@link History} object.
* @param attributeObject value of the {@link Attribute} added to history.
* @param historySize number of items to preserve in the associated {@link History}.
*/
public final History addHistory(final String attributeName, final int size) {
public final void addAttribute(
final String attributeName,
final Object attributeObject,
final int historySize
) {
if (!attributes.containsKey(attributeName)) {
History history = new History(size);
attributes.put(attributeName, history);
attributes.put(attributeName, new History<Attribute>(historySize));
}

return attributes.get(attributeName);
attributes.get(attributeName).add(new Attribute(attributeObject));
}

/**
Expand Down Expand Up @@ -120,12 +127,11 @@ public final Set<String> getAttributes() {
* @return latest {@link Attribute} object from {@link History} if found, null otherwise.
*/
public final Attribute getAttribute(final String attribute) {
History history = attributes.get(attribute);
if (history == null) {
return null;
if (hasAttribute(attribute)) {
return attributes.get(attribute).getLast();
}

return history.getAttribute();
return null;
}

/**
Expand All @@ -137,15 +143,14 @@ public final Attribute getAttribute(final String attribute) {
* @param attribute name of the {@link Attribute} to look up in the map store.
* @param bound size of the resulting array or full history if this exceeds capacity or less than 1.
*
* @return array of the latest {@link Attribute} objects from {@link History} if found, empty otherwise.
* @return {@link List} of the latest {@link Attribute} objects from {@link History} if found, empty otherwise.
*/
public final Attribute[] getAttributes(final String attribute, final int bound) {
History history = attributes.get(attribute);
if (history == null) {
return new Attribute[0];
public final List<Attribute> getAttributes(final String attribute, final int bound) {
if (hasAttribute(attribute)) {
return attributes.get(attribute).get(bound);
}

return history.getAttributes(bound);
return Collections.emptyList();
}

/** {@inheritDoc} */
Expand All @@ -171,11 +176,11 @@ private void buildJson(final JsonGenerator jgen) throws IOException, JsonProcess
int bound = limit.get();

jgen.writeStartObject();
for (Map.Entry<String, History> attributeEntry : attributes.entrySet()) {
for (Map.Entry<String, History<Attribute>> attributeEntry : attributes.entrySet()) {
if (bound < 0) {
jgen.writeObjectField(attributeEntry.getKey(), attributeEntry.getValue().getAttribute());
jgen.writeObjectField(attributeEntry.getKey(), attributeEntry.getValue().getLast());
} else {
jgen.writeObjectField(attributeEntry.getKey(), attributeEntry.getValue().getAttributes(bound));
jgen.writeObjectField(attributeEntry.getKey(), attributeEntry.getValue().get(bound));
}
}
jgen.writeEndObject();
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/com/github/mk23/jmxproxy/jmx/ConnectionWorker.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.github.mk23.jmxproxy.jmx;

import com.github.mk23.jmxproxy.core.History;
import com.github.mk23.jmxproxy.core.Host;
import com.github.mk23.jmxproxy.core.MBean;

Expand Down Expand Up @@ -36,7 +35,8 @@
* to a JMX endpoint. At every execution, discovers and fetches all remote registered mbeans
* and populates the local {@link Host} object with assicated {@link MBean}s and their
* {@link com.github.mk23.jmxproxy.core.Attribute}. Also maintains the {@link Host} access
* time to allow reaping of unaccessed workers.
* time to allow reaping of
* unaccessed workers.
*
* @since 2015-05-11
* @author mk23
Expand Down Expand Up @@ -70,8 +70,8 @@ public class ConnectionWorker {
*
* @param hostName host:port {@link String} JMX agent target.
* @param cacheDuration period in milliseconds for how often to connect to the JMX agent.
* @param historySize number of {@link com.github.mk23.jmxproxy.core.Attribute}s to keep per
* {@link MBean} {@link History}.
* @param historySize number of {@link com.github.mk23.jmxproxy.core.Attribute}s to keep
* per {@link MBean} {@link com.github.mk23.jmxproxy.util.History}.
*
* @throws MalformedURLException if the specified host is not a valid host:port {@link String}.
*/
Expand Down Expand Up @@ -197,8 +197,7 @@ private void fetchJMXValues() {
try {
Object attribute = server.getAttribute(mbeanName, attributeObject.getName());

History history = mbean.addHistory(attributeObject.getName(), historySize);
history.addAttributeValue(attribute);
mbean.addAttribute(attributeObject.getName(), attribute, historySize);
} catch (java.lang.NullPointerException e) {
LOG.error("failed to add attribute " + attributeObject.toString() + ": " + e);
} catch (java.rmi.UnmarshalException e) {
Expand Down
103 changes: 103 additions & 0 deletions src/main/java/com/github/mk23/jmxproxy/util/History.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.github.mk23.jmxproxy.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* <p>Fixed size historical attribute value store.</p>
*
* Saves an object into a fixed size round robin store. Allows clients to retreive latest
* value, a subset of latest values, or the full stored history of values. When number
* of stored values exceeds the fixed size of the store, the oldest values are overwritten.
*
* @param <T> type of objects held in this History.
*
* @since 2015-05-11
* @author mk23
* @version 3.3.2
*/
public class History<T> {
private List<T> objects;
private int length;
private int pointer;
private boolean wrapped;

/**
* <p>Default constructor.</p>
*
* Initializes the {@link Object} store {@link List} to the given history size.
*
* @param length number of {@link Object} values to keep in history.
*/
public History(final int length) {
this.length = length;

pointer = -1;
objects = new ArrayList<T>(Collections.nCopies(length, (T) null));
}

/**
* <p>Adds a item to the store.</p>
*
* Adds a new item to the history store. If the number of items exceeds the size
* of the store, the oldest item is overwritten.
*
* @param item newest item to add to the store.
*/
public final void add(final T item) {
wrapped = ++pointer == length;
pointer = pointer % length;

objects.set(pointer, item);
}

/**
* <p>Gets the latest item from the history store.</p>
*
* Gets the latest single item from the history store.
*
* @return Latest item from the history store or null if empty.
*/
public final T getLast() {
return pointer < 0 ? null : objects.get(pointer % length);
}

/**
* <p>Gets the full item history from the store.</p>
*
* Gets the full item history from the store as a {@link List}.
*
* @return {@link List} of the full history. May be empty if history has no items.
*/
public final List<T> get() {
return get(length);
}

/**
* <p>Gets a limited item history from the store.</p>
*
* Gets a limited history from the store as a {@link List}, based
* on requested item count. If the specified count is less than 0 or greater than
* the number of entries, a full history is returned instead.
*
* @param limit number of items to retreive from the history store.
*
* @return {@link List} of the requested slice of history. May be empty if history has no items.
*/
public final List<T> get(final int limit) {
if (pointer < 0) {
return Collections.emptyList();
}

int head = pointer + length * Boolean.compare(wrapped, false);
int size = Math.min(limit > 0 ? limit : Integer.MAX_VALUE, Math.min((head + 1), length));
List<T> rval = new ArrayList<T>(size);

for (int i = 0; i < size; i++) {
rval.add(objects.get((head - i) % length));
}

return rval;
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/github/mk23/jmxproxy/util/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* <p>Utility classes for the entire application.</p>
*/
package com.github.mk23.jmxproxy.util;
Loading

0 comments on commit 50e3c0f

Please sign in to comment.