commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1395154 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/reloading/ test/java/org/apache/commons/configuration/reloading/
Date Sat, 06 Oct 2012 19:36:03 GMT
Author: oheger
Date: Sat Oct  6 19:36:02 2012
New Revision: 1395154

URL: http://svn.apache.org/viewvc?rev=1395154&view=rev
Log:
Added ReloadingController and event listener interfaces as a generic means to handle reloading.

Added:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
  (with props)
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
  (with props)
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
  (with props)
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
  (with props)

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java?rev=1395154&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
(added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
Sat Oct  6 19:36:02 2012
@@ -0,0 +1,183 @@
+/*
+ * 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.commons.configuration.reloading;
+
+import org.apache.commons.lang3.event.EventListenerSupport;
+
+/**
+ * <p>
+ * A class for adding support for reload operations in a generic way.
+ * </p>
+ * <p>
+ * A {@code ReloadingController} monitors a specific source and triggers
+ * reloading events if necessary. So it does not perform reloading itself, but
+ * only sends out notifications when it thinks that this should be done. This
+ * allows for a very generic setup in which different components involved in
+ * reloading are loosely coupled via events.
+ * </p>
+ * <p>
+ * A typical usage scenario is as follows:
+ * <ul>
+ * <li>A {@code ReloadingController} instance is created and initialized with a
+ * {@link ReloadingDetector} object.</li>
+ * <li>A number of {@link ReloadingListener} objects can be registered at the
+ * controller.</li>
+ * <li>Now the controller's {@code checkForReloading()} method is called
+ * whenever a check is to be performed. This could be done for instance by a
+ * timer in regular intervals or by any other means appropriate for a specific
+ * application.</li>
+ * <li>When a check reveals that a reload operation is necessary all registered
+ * event listeners are notified.</li>
+ * <li>Typically one of the listeners is responsible to perform the actual
+ * reload operation. (How this is done is not in the scope of the controller
+ * object.) After this has been done, the controller's
+ * {@code resetReloadingState()} method must be called. It tells the controller
+ * that the last notification has been processed and that new checks are
+ * possible again. It is important that this method is called. Otherwise,
+ * {@code checkForReloading()} will not do any new checks or send out event
+ * notifications any more.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class can be accessed from multiple threads concurrently. It shields the
+ * associated {@link ReloadingDetector} object for concurrent access, so that a
+ * concrete detector implementation does not have to be thread-safe.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public class ReloadingController
+{
+    /** Stores a reference to the reloading detector. */
+    private final ReloadingDetector detector;
+
+    /** The helper object which manages the registered event listeners. */
+    private final EventListenerSupport<ReloadingListener> listeners;
+
+    /** A flag whether this controller is in reloading state. */
+    private boolean reloadingState;
+
+    /**
+     * Creates a new instance of {@code ReloadingController} and associates it
+     * with the given {@code ReloadingDetector} object.
+     *
+     * @param detect the {@code ReloadingDetector} (must not be <b>null</b>)
+     * @throws IllegalArgumentException if the detector is undefined
+     */
+    public ReloadingController(ReloadingDetector detect)
+    {
+        if (detect == null)
+        {
+            throw new IllegalArgumentException(
+                    "ReloadingDetector must not be null!");
+        }
+
+        detector = detect;
+        listeners = EventListenerSupport.create(ReloadingListener.class);
+    }
+
+    /**
+     * Returns the {@code ReloadingDetector} used by this controller.
+     *
+     * @return the {@code ReloadingDetector}
+     */
+    public ReloadingDetector getDetector()
+    {
+        return detector;
+    }
+
+    /**
+     * Adds the specified {@code ReloadingListener} to this controller. It will
+     * receive notifications whenever a reload operation is necessary.
+     *
+     * @param l the listener to be added (must not be <b>null</b>)
+     */
+    public void addReloadingListener(ReloadingListener l)
+    {
+        listeners.addListener(l);
+    }
+
+    /**
+     * Removes the specified {@code ReloadingListener} from this controller.
+     *
+     * @param l
+     */
+    public void removeReloadingListener(ReloadingListener l)
+    {
+        listeners.removeListener(l);
+    }
+
+    /**
+     * Tests whether this controller is in <em>reloading state</em>. A return
+     * value of <b>true</b> means that a previous invocation of
+     * {@code checkForReloading()} has detected the necessity for a reload
+     * operation, but {@code resetReloadingState()} has not been called yet. In
+     * this state no further reloading checks are possible.
+     *
+     * @return a flag whether this controller is in reloading state
+     */
+    public synchronized boolean isInReloadingState()
+    {
+        return reloadingState;
+    }
+
+    /**
+     * Performs a check whether a reload operation is necessary. This method has
+     * to be called to trigger the generation of reloading events. It delegates
+     * to the associated {@link ReloadingDetector} and sends out notifications
+     * if necessary. The argument can be an arbitrary data object; it will be
+     * part of the event notification sent out when a reload operation should be
+     * performed.
+     *
+     * @param data additional data for an event notification
+     */
+    public void checkForReloading(Object data)
+    {
+        boolean sendEvent = false;
+        synchronized (this)
+        {
+            if (!isInReloadingState())
+            {
+                if (getDetector().isReloadingRequired())
+                {
+                    sendEvent = true;
+                    reloadingState = true;
+                }
+            }
+        }
+
+        if (sendEvent)
+        {
+            listeners.fire().reloadingRequired(new ReloadingEvent(this, data));
+        }
+    }
+
+    /**
+     * Resets the reloading state. This tells the controller that reloading has
+     * been performed and new checks are possible again. If this controller is
+     * not in reloading state, this method has no effect.
+     */
+    public synchronized void resetReloadingState()
+    {
+        if (isInReloadingState())
+        {
+            getDetector().reloadingPerformed();
+            reloadingState = false;
+        }
+    }
+}

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingController.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java?rev=1395154&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
(added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
Sat Oct  6 19:36:02 2012
@@ -0,0 +1,79 @@
+/*
+ * 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.commons.configuration.reloading;
+
+import java.util.EventObject;
+
+/**
+ * <p>
+ * An event that is fired when a reload operation is required.
+ * </p>
+ * <p>
+ * Events of this type are generated by {@link ReloadingController} if the need
+ * for a reload operation is detected. From the pay-load of the event
+ * information about the components involved is available.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public class ReloadingEvent extends EventObject
+{
+    /**
+     * The serial version UID.
+     */
+    private static final long serialVersionUID = 20121006L;
+
+    /** Stores additional data about this event. */
+    private final Object data;
+
+    /**
+     * Creates a new instance of {@code ReloadingEvent} and initializes it.
+     *
+     * @param source the controller which generated this event
+     * @param addData an arbitrary data object to be evaluated by event
+     *        listeners
+     */
+    public ReloadingEvent(ReloadingController source, Object addData)
+    {
+        super(source);
+        data = addData;
+    }
+
+    /**
+     * Returns the {@code ReloadingController} which caused this event.
+     *
+     * @return the responsible {@code ReloadingController}
+     */
+    public ReloadingController getController()
+    {
+        return (ReloadingController) getSource();
+    }
+
+    /**
+     * Returns an object with additional data about the reload operation. This
+     * is the object that was passed to the {@link ReloadingController} when it
+     * was asked to do a reloading check. This is a generic mechanism to pass
+     * arbitrary data to reloading listeners.
+     *
+     * @return additional data about the reload operation (can be <b>null</b>)
+     */
+    public Object getData()
+    {
+        return data;
+    }
+}

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingEvent.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java?rev=1395154&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
(added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
Sat Oct  6 19:36:02 2012
@@ -0,0 +1,47 @@
+/*
+ * 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.commons.configuration.reloading;
+
+import java.util.EventListener;
+
+/**
+ * <p>
+ * An event listener interface to be implemented by objects that want to be
+ * notified about reloading events.
+ * </p>
+ * <p>
+ * Objects implementing this interface can be registered at a
+ * {@link ReloadingController}. They are then notified whenever a check of the
+ * controller detects that a reload operation should be performed. This can be
+ * useful for instance to find out that configuration settings might have
+ * changed.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public interface ReloadingListener extends EventListener
+{
+    /**
+     * Notifies this listener that a {@link ReloadingController} detected the
+     * necessity to reload data. More details are available through the passed
+     * in {@code ReloadingEvent} object.
+     *
+     * @param event a {@code ReloadingEvent} with details
+     */
+    void reloadingRequired(ReloadingEvent event);
+}

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/reloading/ReloadingListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java?rev=1395154&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
(added)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
Sat Oct  6 19:36:02 2012
@@ -0,0 +1,184 @@
+/*
+ * 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.commons.configuration.reloading;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@code ReloadingController}.
+ *
+ * @version $Id$
+ */
+public class TestReloadingController
+{
+    /** A mock for the detector. */
+    private ReloadingDetector detector;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        detector = EasyMock.createMock(ReloadingDetector.class);
+    }
+
+    /**
+     * Creates a default test instance.
+     *
+     * @return the test instance
+     */
+    private ReloadingController createController()
+    {
+        return new ReloadingController(detector);
+    }
+
+    /**
+     * Tries to create an instance without a detector.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitNoDetector()
+    {
+        new ReloadingController(null);
+    }
+
+    /**
+     * Tests that a newly created instance is not in reloading state.
+     */
+    @Test
+    public void testReloadingStateAfterInit()
+    {
+        assertFalse("In reloading state", createController()
+                .isInReloadingState());
+    }
+
+    /**
+     * Prepares the given event listener mock to expect an event notification.
+     * The event received is stored in the given mutable object.
+     *
+     * @param l the listener mock
+     * @param evRef the reference where to store the event
+     */
+    private void expectEvent(ReloadingListener l,
+            final MutableObject<ReloadingEvent> evRef)
+    {
+        l.reloadingRequired(EasyMock.anyObject(ReloadingEvent.class));
+        EasyMock.expectLastCall().andAnswer(new IAnswer<Object>()
+        {
+            public Object answer() throws Throwable
+            {
+                evRef.setValue((ReloadingEvent) EasyMock.getCurrentArguments()[0]);
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Tests a reloading check with a positive result.
+     */
+    @Test
+    public void testCheckForReloadingTrue()
+    {
+        ReloadingListener l = EasyMock.createMock(ReloadingListener.class);
+        ReloadingListener lRemoved =
+                EasyMock.createMock(ReloadingListener.class);
+        MutableObject<ReloadingEvent> evRef =
+                new MutableObject<ReloadingEvent>();
+        expectEvent(l, evRef);
+        EasyMock.expect(detector.isReloadingRequired()).andReturn(Boolean.TRUE);
+        EasyMock.replay(detector, l, lRemoved);
+        ReloadingController ctrl = createController();
+        ctrl.addReloadingListener(lRemoved);
+        ctrl.addReloadingListener(l);
+        ctrl.removeReloadingListener(lRemoved);
+        Object testData = "Some test data";
+        ctrl.checkForReloading(testData);
+        assertTrue("Not in reloading state", ctrl.isInReloadingState());
+        assertSame("Wrong event source", ctrl, evRef.getValue().getSource());
+        assertSame("Wrong controller", ctrl, evRef.getValue().getController());
+        assertEquals("Wrong event data", testData, evRef.getValue().getData());
+        EasyMock.verify(l, lRemoved, detector);
+    }
+
+    /**
+     * Tests a reloading check with a negative result.
+     */
+    @Test
+    public void testCheckForReloadingFalse()
+    {
+        ReloadingListener l = EasyMock.createMock(ReloadingListener.class);
+        EasyMock.expect(detector.isReloadingRequired())
+                .andReturn(Boolean.FALSE);
+        EasyMock.replay(detector, l);
+        ReloadingController ctrl = createController();
+        ctrl.addReloadingListener(l);
+        ctrl.checkForReloading(null);
+        assertFalse("In reloading state", ctrl.isInReloadingState());
+        EasyMock.verify(detector, l);
+    }
+
+    /**
+     * Tests that no further checks are performed when already in reloading
+     * state.
+     */
+    @Test
+    public void testCheckForReloadingInReloadingState()
+    {
+        ReloadingListener l = EasyMock.createMock(ReloadingListener.class);
+        EasyMock.expect(detector.isReloadingRequired()).andReturn(Boolean.TRUE);
+        expectEvent(l, new MutableObject<ReloadingEvent>());
+        EasyMock.replay(detector, l);
+        ReloadingController ctrl = createController();
+        ctrl.addReloadingListener(l);
+        ctrl.checkForReloading(1);
+        ctrl.checkForReloading(2);
+        EasyMock.verify(detector, l);
+    }
+
+    /**
+     * Tests that the reloading state can be reset.
+     */
+    @Test
+    public void testResetReloadingState()
+    {
+        EasyMock.expect(detector.isReloadingRequired()).andReturn(Boolean.TRUE);
+        detector.reloadingPerformed();
+        EasyMock.replay(detector);
+        ReloadingController ctrl = createController();
+        ctrl.checkForReloading(null);
+        ctrl.resetReloadingState();
+        assertFalse("In reloading state", ctrl.isInReloadingState());
+        EasyMock.verify(detector);
+    }
+
+    /**
+     * Tests that resetReloadingState() has no effect if the controller is not
+     * in reloading state.
+     */
+    @Test
+    public void testResetReloadingNotInReloadingState()
+    {
+        EasyMock.replay(detector);
+        createController().resetReloadingState();
+    }
+}

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/reloading/TestReloadingController.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message