View Javadoc

1   /**
2    * This file Copyright (c) 2003-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.util;
35  
36  import java.util.ArrayList;
37  import java.util.Collections;
38  import java.util.List;
39  import javax.jcr.Node;
40  import javax.jcr.RepositoryException;
41  import javax.jcr.Session;
42  import javax.jcr.observation.EventIterator;
43  import javax.jcr.observation.EventListener;
44  
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  import info.magnolia.context.MgnlContext;
49  import info.magnolia.module.ModuleRegistry;
50  import info.magnolia.repository.RepositoryConstants;
51  
52  
53  /**
54   * Base class for managers that monitor configuration within modules and react when its changed to reload. Configuration
55   * is read from a sub path of each modules configuration node. Subclasses override either reload(List<Node>) or the pair
56   * onClear() and onRegister().
57   *
58   * @version $Id$
59   */
60  public abstract class ModuleConfigurationObservingManager {
61  
62      protected final Logger log = LoggerFactory.getLogger(getClass());
63  
64      private final Object reloadMonitor = new Object();
65      private final String pathWithinModule;
66      private final ModuleRegistry moduleRegistry;
67      private final List<String> observedPaths = new ArrayList<String>();
68  
69      protected ModuleConfigurationObservingManager(String pathWithinModule, ModuleRegistry moduleRegistry) {
70          this.pathWithinModule = pathWithinModule;
71          this.moduleRegistry = moduleRegistry;
72      }
73  
74      public void start() {
75  
76          for (String moduleName : moduleRegistry.getModuleNames()) {
77              String path = "/modules/" + moduleName + "/" + pathWithinModule;
78              observedPaths.add(path);
79          }
80  
81          final EventListener eventListener = ObservationUtil.instanciateDeferredEventListener(new EventListener() {
82  
83              @Override
84              public void onEvent(EventIterator events) {
85                  synchronized (reloadMonitor) {
86                      MgnlContext.doInSystemContext(new MgnlContext.VoidOp(){
87                          @Override
88                          public void doExec() {
89                              reload();
90                          }
91                      }, true);
92                  }
93              }
94          }, 1000, 5000);
95  
96          for (String observedPath : observedPaths) {
97              ObservationUtil.registerChangeListener(RepositoryConstants.CONFIG, observedPath, new EventListener(){
98  
99                  @Override
100                 public void onEvent(EventIterator events) {
101                     eventListener.onEvent(events);
102                 }
103             });
104         }
105 
106         synchronized (reloadMonitor) {
107             reload();
108         }
109     }
110 
111     protected void reload() {
112         try {
113             List<Node> nodes = getObservedNodes();
114             reload(nodes);
115         } catch (RepositoryException e) {
116             log.error("Reload of observed nodes failed", e);
117         }
118     }
119 
120     protected void reload(List<Node> nodes) throws RepositoryException {
121         onClear();
122         for (Node node : nodes) {
123             try {
124                 onRegister(node);
125             } catch (Exception e) {
126                 log.warn("Failed to reload the node [" + node.getPath() + "]");
127             }
128         }
129     }
130 
131     protected void onClear() throws RepositoryException {
132     }
133 
134     protected void onRegister(Node node) throws RepositoryException {
135     }
136 
137     private List<Node> getObservedNodes() throws RepositoryException {
138 
139         Session session = getSession();
140 
141         List<Node> nodes = new ArrayList<Node>();
142         for (String observedPath : observedPaths) {
143             try {
144                 if (session.nodeExists(observedPath)) {
145                     nodes.add(session.getNode(observedPath));
146                 }
147             } catch (RepositoryException e) {
148                 log.error("Failed to acquire node for observed path [" + observedPath + "]", e);
149             }
150         }
151         return nodes;
152     }
153 
154     protected Session getSession() throws RepositoryException {
155         return MgnlContext.getSystemContext().getJCRSession(RepositoryConstants.CONFIG);
156     }
157 
158     protected List<String> getObservedPaths() {
159         return Collections.unmodifiableList(observedPaths);
160     }
161 
162     /**
163      * Returns the object used to synchronize on to keep multiple reload operations to occur at the same time. Useful
164      * in subclasses to block calls while the reload is in progress.
165      */
166     protected Object getReloadMonitor() {
167         return reloadMonitor;
168     }
169 }