Skip to content

Commit

Permalink
Implemented "NotChangedException" when detecting that no response is …
Browse files Browse the repository at this point in the history
…required #77
  • Loading branch information
rhuss committed Mar 7, 2013
1 parent e3a05b4 commit 18661fd
Show file tree
Hide file tree
Showing 18 changed files with 161 additions and 94 deletions.
23 changes: 17 additions & 6 deletions agent/core/src/main/java/org/jolokia/backend/BackendManager.java
Expand Up @@ -7,6 +7,7 @@

import javax.management.*;

import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.config.ConfigKey;
import org.jolokia.config.Configuration;
import org.jolokia.converter.Converters;
Expand Down Expand Up @@ -144,11 +145,21 @@ public JSONObject handleRequest(JmxRequest pJmxReq) throws InstanceNotFoundExcep
if (debug) {
time = System.currentTimeMillis();
}
JSONObject json = callRequestDispatcher(pJmxReq);

// Update global history store
historyStore.updateAndAdd(pJmxReq,json);
json.put("status",200 /* success */);
JSONObject json = null;
try {
json = callRequestDispatcher(pJmxReq);

// Update global history store, add timestamp and possibly history information to the request
historyStore.updateAndAdd(pJmxReq,json);
json.put("status",200 /* success */);
} catch (NotChangedException exp) {
// A handled indicates that its value hasn't changed. We return an status with
//"304 Not Modified" similar to the HTTP status code (http://en.wikipedia.org/wiki/HTTP_status)
json = new JSONObject();
json.put("request",pJmxReq.toJSON());
json.put("status",304);
json.put("timestamp",System.currentTimeMillis() / 1000);
}

if (debug) {
debug("Execution time: " + (System.currentTimeMillis() - time) + " ms");
Expand Down Expand Up @@ -368,7 +379,7 @@ private RequestDispatcher createDispatcher(String pDispatcherClass,

// call the an appropriate request dispatcher
private JSONObject callRequestDispatcher(JmxRequest pJmxReq)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException {
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException, NotChangedException {
Object retValue = null;
boolean useValueWithPath = false;
boolean found = false;
Expand Down
Expand Up @@ -20,6 +20,7 @@

import javax.management.*;

import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.config.ConfigKey;
import org.jolokia.config.Configuration;
import org.jolokia.converter.Converters;
Expand Down Expand Up @@ -85,7 +86,7 @@ public boolean useReturnValueWithPath(JmxRequest pJmxRequest) {

/** {@inheritDoc} */
public Object dispatchRequest(JmxRequest pJmxReq)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException {
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, NotChangedException {
JsonRequestHandler handler = requestHandlerManager.getRequestHandler(pJmxReq.getType());
return mBeanServerHandler.dispatchRequest(handler, pJmxReq);
}
Expand Down
Expand Up @@ -22,6 +22,7 @@
import javax.management.*;

import org.jolokia.backend.executor.AbstractMBeanServerExecutor;
import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.detector.ServerDetector;
import org.jolokia.handler.JsonRequestHandler;
import org.jolokia.request.JmxRequest;
Expand Down Expand Up @@ -99,7 +100,7 @@ private synchronized void init(List<ServerDetector> pDetectors) {
* @throws InstanceNotFoundException
*/
public <R extends JmxRequest> Object handleRequest(JsonRequestHandler<R> pRequestHandler, R pJmxReq)
throws MBeanException, ReflectionException, AttributeNotFoundException, InstanceNotFoundException {
throws MBeanException, ReflectionException, AttributeNotFoundException, InstanceNotFoundException, NotChangedException {
AttributeNotFoundException attrException = null;
InstanceNotFoundException objNotFoundException = null;

Expand Down
Expand Up @@ -7,6 +7,7 @@
import javax.management.*;

import org.jolokia.backend.executor.MBeanServerExecutor;
import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.config.ConfigKey;
import org.jolokia.config.Configuration;
import org.jolokia.detector.*;
Expand Down Expand Up @@ -89,7 +90,7 @@ private void initServerHandle(Configuration pConfig, LogHandler pLogHandler, Lis
* @return the result of the request
*/
public Object dispatchRequest(JsonRequestHandler pRequestHandler, JmxRequest pJmxReq)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException {
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, NotChangedException {
serverHandle.preDispatch(mBeanServerManager,pJmxReq);
if (pRequestHandler.handleAllServersAtOnce(pJmxReq)) {
try {
Expand Down
Expand Up @@ -4,6 +4,7 @@

import javax.management.*;

import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.request.JmxRequest;

/*
Expand Down Expand Up @@ -42,7 +43,7 @@ public interface RequestDispatcher {
* @throws MBeanException
*/
Object dispatchRequest(JmxRequest pJmxReq)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException;
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException, NotChangedException;

/**
* Check whether current dispatcher can handle the given request
Expand Down
Expand Up @@ -32,7 +32,7 @@
public abstract class AbstractMBeanServerExecutor implements MBeanServerExecutor, NotificationListener {

// Timestamp of last MBeanServer change in milliseconds
private long lastMBeanServerChange;
private long lastMBeanRegistrationChange;

/**
* Get all MBeanServers
Expand Down Expand Up @@ -118,9 +118,6 @@ public Set<ObjectName> queryNames(ObjectName pObjectName) throws IOException {
/**
* Add this executor as listener for MBeanServer notification so that we can update
* the local timestamp for when the set of registered MBeans has changed last.
*
* @throws IOException
* @throws InstanceNotFoundException
*/
protected void registerForMBeanNotifications() {
Set<MBeanServerConnection> servers = getMBeanServers();
Expand All @@ -139,7 +136,7 @@ protected void registerForMBeanNotifications() {
}

/**
* Deregister ourself as listener from every registered server
* Unregister us as listener from every registered server
*/
public void destroy() {
Set<MBeanServerConnection> servers = getMBeanServers();
Expand All @@ -157,34 +154,21 @@ public void destroy() {
}
}

private Exception updateErrorMsg(StringBuilder pErrors, Exception exp) {
pErrors.append(exp.getClass()).append(": ").append(exp.getMessage()).append("\n");
return exp;
}

/** {@inheritDoc} */
// Remember current timestamp
public void handleNotification(Notification pNotification, Object pHandback) {
// Update timestamp
lastMBeanServerChange = System.currentTimeMillis();
lastMBeanRegistrationChange = System.currentTimeMillis();
}

/**
* Check whether the set of MBeans in all managed MBeanServer has been changed
* since the given time. The input is the epoch time in seconds, however, milliseconds
* would be much more appropriate. However, the Jolokia responses contain
* currently time measured in seconds. This should be changed in a future version,
* but this implies a quite heavy API changed (and if this is changed, the key
* "timestamp" should be changed to "time", too, in order to fail early in case of
* problems).
*
* In order to avoid inconsistencies for sub-second updates, we are comparing
* conservatively (so hasBeenUpdated might return "true" more often than required).
*
* @param pTimestamp seconds since 1.1.1970
* @return true if the MBeans has been updated since this time, false otherwise
*/
public boolean hasBeenUpdatedSince(long pTimestamp) {
return (lastMBeanServerChange / 1000) >= pTimestamp;
/** {@inheritDoc} */
public boolean hasMBeansListChangedSince(long pTimestamp) {
return (lastMBeanRegistrationChange / 1000) >= pTimestamp;
}

// Helper method for adding the exception for an appropriate error message
private Exception updateErrorMsg(StringBuilder pErrors, Exception exp) {
pErrors.append(exp.getClass()).append(": ").append(exp.getMessage()).append("\n");
return exp;
}
}
Expand Up @@ -85,6 +85,23 @@ <R> R call(ObjectName pObjectName, MBeanAction<R> pMBeanAction, Object... pExtra
*/
void destroy();

/**
* Check whether the set of MBeans in all managed MBeanServer has been changed
* since the given time. The input is the epoch time in seconds, however, milliseconds
* would be much more appropriate. However, the Jolokia responses contain
* currently time measured in seconds. This should be changed in a future version,
* but this implies a quite heavy API changed (and if this is changed, the key
* "timestamp" should be changed to "time", too, in order to fail early in case of
* problems).
*
* In order to avoid inconsistencies for sub-second updates, we are comparing
* conservatively (so hasBeenUpdated might return "true" more often than required).
*
* @param pTimestamp seconds since 1.1.1970
* @return true if the MBeans has been updated since this time, false otherwise
*/
boolean hasMBeansListChangedSince(long pTimestamp);

/**
* This callback is used together with {@link #each(ObjectName, MBeanEachCallback)} for iterating over all
* active MBeanServers. The callback is responsible on its own to collect the information queried.
Expand Down
@@ -0,0 +1,30 @@
package org.jolokia.backend.executor;

import org.jolokia.request.JmxRequest;

/**
* Exception thrown when an <code>ifModifiedSince</code> parameter was given and
* the requested resourced doesnt has changed
* @author roland
* @since 07.03.13
*/
public class NotChangedException extends Exception {

private JmxRequest request;

/**
* Constructor
* @param pRequest which lead to this exception
*/
public NotChangedException(JmxRequest pRequest) {
request = pRequest;
}

/**
* Request which lead to this exception
* @return request
*/
public JmxRequest getRequest() {
return request;
}
}
Expand Up @@ -5,6 +5,7 @@
import javax.management.*;

import org.jolokia.backend.executor.MBeanServerExecutor;
import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.request.JmxRequest;
import org.jolokia.restrictor.Restrictor;
import org.jolokia.util.RequestType;
Expand Down Expand Up @@ -81,7 +82,7 @@ public boolean handleAllServersAtOnce(R pRequest) {
* @throws java.io.IOException
*/
public Object handleRequest(MBeanServerConnection pServer, R pRequest)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException {
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException, NotChangedException {
checkForRestriction(pRequest);
checkHttpMethod(pRequest);
return doHandleRequest(pServer, pRequest);
Expand Down Expand Up @@ -136,7 +137,7 @@ private void checkHttpMethod(R pRequest) {
* @throws IOException
*/
protected abstract Object doHandleRequest(MBeanServerConnection server, R request)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException;
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException, NotChangedException;

/**
* Override this if you want to have all servers at once for processing the request
Expand All @@ -154,7 +155,7 @@ protected abstract Object doHandleRequest(MBeanServerConnection server, R reques
* @throws ReflectionException
*/
public Object handleRequest(MBeanServerExecutor pServerManager, R request)
throws ReflectionException, InstanceNotFoundException, MBeanException, AttributeNotFoundException, IOException {
throws ReflectionException, InstanceNotFoundException, MBeanException, AttributeNotFoundException, IOException, NotChangedException {
checkForRestriction(request);
return doHandleRequest(pServerManager,request);
}
Expand All @@ -174,7 +175,7 @@ public Object handleRequest(MBeanServerExecutor pServerManager, R request)
* @throws ReflectionException
*/
public Object doHandleRequest(MBeanServerExecutor serverManager, R request)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException {
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException, IOException, NotChangedException {
return null;
}

Expand Down
17 changes: 15 additions & 2 deletions agent/core/src/main/java/org/jolokia/handler/ListHandler.java
Expand Up @@ -7,6 +7,7 @@
import javax.management.*;

import org.jolokia.backend.executor.MBeanServerExecutor;
import org.jolokia.backend.executor.NotChangedException;
import org.jolokia.config.ConfigKey;
import org.jolokia.handler.list.MBeanInfoData;
import org.jolokia.request.JmxListRequest;
Expand Down Expand Up @@ -73,7 +74,10 @@ protected void checkForRestriction(JmxListRequest pRequest) {
/** {@inheritDoc} */
@Override
public Object doHandleRequest(MBeanServerExecutor pServerManager, JmxListRequest pRequest)
throws IOException {
throws IOException, NotChangedException {
// Throw an exception if list has not changed
checkForModifiedSince(pServerManager, pRequest);

Stack<String> originalPathStack = EscapeUtil.reversePath(pRequest.getPathParts());
int maxDepth = pRequest.getParameterAsInt(ConfigKey.MAX_DEPTH);
boolean useCanonicalName = pRequest.getParameterAsBool(ConfigKey.CANONICAL_NAMING);
Expand Down Expand Up @@ -101,14 +105,15 @@ public Object doHandleRequest(MBeanServerExecutor pServerManager, JmxListRequest
}

// will not be called

/** {@inheritDoc} */
@Override
public Object doHandleRequest(MBeanServerConnection server, JmxListRequest request)
throws InstanceNotFoundException, AttributeNotFoundException, ReflectionException, MBeanException {
throw new UnsupportedOperationException("Internal: Method must not be called when all MBeanServers are handled at once");
}

@Override

/**
* Path handling is done directly within this handler to avoid
* excessive memory consumption by building up the whole list
Expand All @@ -122,6 +127,14 @@ public boolean useReturnValueWithPath() {

// ==========================================================================================================

// check for freshness
private void checkForModifiedSince(MBeanServerExecutor pServerManager, JmxListRequest pRequest) throws NotChangedException {
int ifModifiedSince = pRequest.getParameterAsInt(ConfigKey.IF_MODIFIED_SINCE);
if (!pServerManager.hasMBeansListChangedSince(ifModifiedSince)) {
throw new NotChangedException(pRequest);
}
}

/**
* Prepare an objectname patttern from a path (or "null" if no path is given)
* @param pPathStack path
Expand Down

0 comments on commit 18661fd

Please sign in to comment.