1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.blossom.context;
35
36 import javax.jcr.Node;
37 import javax.jcr.RepositoryException;
38 import javax.jcr.observation.EventIterator;
39 import javax.jcr.observation.EventListener;
40
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.aop.TargetSource;
44 import org.springframework.aop.framework.ProxyFactoryBean;
45 import org.springframework.beans.factory.BeanFactory;
46 import org.springframework.beans.factory.BeanFactoryAware;
47 import org.springframework.beans.factory.BeanNameAware;
48 import org.springframework.beans.factory.DisposableBean;
49 import org.springframework.beans.factory.InitializingBean;
50 import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
51 import org.springframework.context.ApplicationContext;
52 import org.springframework.context.ApplicationContextAware;
53 import org.springframework.context.ApplicationEventPublisher;
54 import org.springframework.context.ApplicationEventPublisherAware;
55
56 import info.magnolia.cms.util.ContentUtil;
57 import info.magnolia.cms.util.ObservationUtil;
58 import info.magnolia.content2bean.Content2BeanException;
59 import info.magnolia.content2bean.Content2BeanUtil;
60 import info.magnolia.context.SystemContext;
61 import info.magnolia.module.blossom.content2bean.SpringContent2BeanTransformer;
62 import info.magnolia.module.blossom.support.BeanFactoryUtils;
63 import info.magnolia.objectfactory.Components;
64 import info.magnolia.repository.RepositoryConstants;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public class ObservedBeanFactoryBean extends ProxyFactoryBean implements InitializingBean, BeanNameAware, ApplicationContextAware, BeanFactoryAware, EventListener, DisposableBean, ApplicationEventPublisherAware {
81
82 private static final Logger logger = LoggerFactory.getLogger(ObservedBeanFactoryBean.class);
83
84 private static final String DEFAULT_WORKSPACE = RepositoryConstants.CONFIG;
85 private static final int DEFAULT_MAX_DELAY = 60000;
86 private static final int DEFAULT_DELAY = 15000;
87
88 private ApplicationContext applicationContext;
89 private ApplicationEventPublisher applicationEventPublisher;
90 private AbstractAutowireCapableBeanFactory beanFactory;
91 private String beanName;
92 private String workspace = DEFAULT_WORKSPACE;
93 private String path;
94 private int maxObservationDelay = DEFAULT_MAX_DELAY;
95 private int observationDelay = DEFAULT_DELAY;
96 private Class<?> defaultClass;
97
98 private volatile Object target;
99
100 public ObservedBeanFactoryBean() {
101 super.setSingleton(true);
102
103 super.setTargetSource(new TargetSource() {
104
105 @Override
106 public Class<?> getTargetClass() {
107 Object local = target;
108 return local != null ? local.getClass() : null;
109 }
110
111 @Override
112 public boolean isStatic() {
113 return false;
114 }
115
116 @Override
117 public Object getTarget() throws Exception {
118 return target;
119 }
120
121 @Override
122 public void releaseTarget(Object target) throws Exception {
123 }
124 });
125 }
126
127 public Class<?> getDefaultClass() {
128 return defaultClass;
129 }
130
131 public void setDefaultClass(Class<?> defaultClass) {
132 this.defaultClass = defaultClass;
133 }
134
135 @Deprecated
136 public String getRepository() {
137 return workspace;
138 }
139
140 @Deprecated
141 public void setRepository(String repository) {
142 this.workspace = repository;
143 }
144
145 public String getWorkspace() {
146 return workspace;
147 }
148
149 public void setWorkspace(String workspace) {
150 this.workspace = workspace;
151 }
152
153 public String getPath() {
154 return path;
155 }
156
157 public void setPath(String path) {
158 this.path = path;
159 }
160
161 public void setMaxObservationDelay(int maxObservationDelay) {
162 this.maxObservationDelay = maxObservationDelay;
163 }
164
165 public void setObservationDelay(int observationDelay) {
166 this.observationDelay = observationDelay;
167 }
168
169 @Override
170 public void afterPropertiesSet() throws Exception {
171 this.target = createInstance();
172 startObservation();
173 }
174
175 @Override
176 public void destroy() throws Exception {
177 stopObservation();
178 Object instance = target;
179 target = null;
180 destroyInstance(instance);
181 }
182
183 @Override
184 public void onEvent(EventIterator events) {
185 try {
186 reloadInstance();
187 } catch (Exception e) {
188 logger.error("Failed to reload observed bean [" + beanName + "] configured at path [" + path + "] in workspace [" + workspace + "] on change event", e);
189 }
190 }
191
192 @Override
193 public void setApplicationContext(ApplicationContext applicationContext) {
194 this.applicationContext = applicationContext;
195 }
196
197 @Override
198 public void setBeanFactory(BeanFactory beanFactory) {
199 this.beanFactory = (AbstractAutowireCapableBeanFactory) beanFactory;
200 }
201
202 @Override
203 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
204 this.applicationEventPublisher = applicationEventPublisher;
205 }
206
207 @Override
208 public void setBeanName(String beanName) {
209 this.beanName = beanName;
210 }
211
212 protected Object createInstance() throws Content2BeanException, RepositoryException {
213 Node node = getConfigurationNode();
214 return transformNode(node);
215 }
216
217 protected void destroyInstance(Object instance) {
218 if (instance != null) {
219 BeanFactoryUtils.destroyBean(instance, beanName, beanFactory);
220 }
221 }
222
223 protected synchronized void reloadInstance() throws Content2BeanException, RepositoryException {
224 Object previousInstance = this.target;
225 this.target = createInstance();
226 destroyInstance(previousInstance);
227
228 applicationEventPublisher.publishEvent(new ObservedBeanReloadedEvent(applicationContext, target, beanName, workspace, path));
229 }
230
231 protected Object transformNode(Node node) throws Content2BeanException {
232 SpringContent2BeanTransformer transformer = new SpringContent2BeanTransformer(beanFactory);
233 transformer.setTopLevelBeanName(beanName);
234 transformer.setDefaultClass(defaultClass);
235 return Content2BeanUtil.toBean(ContentUtil.asContent(node), true, transformer);
236 }
237
238 protected Node getConfigurationNode() throws RepositoryException {
239 return Components.getComponent(SystemContext.class).getJCRSession(workspace).getNode(path);
240 }
241
242 protected void startObservation() {
243 ObservationUtil.registerDeferredChangeListener(workspace, path, this, observationDelay, maxObservationDelay);
244 }
245
246 protected void stopObservation() {
247 ObservationUtil.unregisterChangeListener(workspace, this);
248 }
249 }