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.objectfactory;
35
36 import info.magnolia.cms.security.SilentSessionOp;
37 import info.magnolia.cms.util.ObservationUtil;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.jcr.node2bean.Node2BeanException;
40 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
41 import info.magnolia.jcr.node2bean.Node2BeanTransformer;
42 import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
43
44 import javax.jcr.Node;
45 import javax.jcr.RepositoryException;
46 import javax.jcr.Session;
47 import javax.jcr.observation.EventIterator;
48 import javax.jcr.observation.EventListener;
49
50 import org.apache.commons.proxy.ObjectProvider;
51 import org.apache.commons.proxy.factory.cglib.CglibProxyFactory;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60 public class ObservedComponentFactory<T> implements ComponentFactory<T>, EventListener {
61 private static final Logger log = LoggerFactory.getLogger(ObservedComponentFactory.class);
62
63 private static final int DEFAULT_MAX_DELAY = 5000;
64 private static final int DEFAULT_DELAY = 1000;
65
66
67
68
69 private final String repository;
70
71
72
73
74 private final String path;
75
76 private final Class<T> type;
77
78
79
80
81 private T observedObject;
82
83 private ComponentProvider componentProvider;
84
85 public ObservedComponentFactory(String repository, String path, Class<T> type) {
86 this(repository, path, type, Components.getComponentProvider());
87 }
88
89 public ObservedComponentFactory(String repository, String path, Class<T> type, ComponentProvider componentProvider) {
90 this.repository = repository;
91 this.path = path;
92 this.type = type;
93 this.componentProvider = componentProvider;
94 load();
95 startObservation(path);
96 }
97
98 @Override
99 @SuppressWarnings("unchecked")
100
101 public T newInstance() {
102 if (getObservedObject() == null) {
103
104
105 log.warn("An instance of {} couldn't be loaded from {}:{} yet, returning null.", type, repository, path);
106 return null;
107 }
108
109 return (T) new CglibProxyFactory().createDelegatorProxy(new ObjectProvider() {
110 @Override
111 public Object getObject() {
112 return getObservedObject();
113 }
114 }, getExposedInterfaces());
115 }
116
117 protected Class[] getExposedInterfaces() {
118 return new Class[]{
119
120 getObservedObject().getClass()
121 };
122 }
123
124 protected void startObservation(String handle) {
125 ObservationUtil.registerDeferredChangeListener(repository, handle, this, DEFAULT_DELAY, DEFAULT_MAX_DELAY);
126 }
127
128 @Override
129 public void onEvent(EventIterator events) {
130 reload();
131 }
132
133 protected void reload() {
134 load();
135 }
136
137 protected void load() {
138 MgnlContext.doInSystemContext(new SilentSessionOp<Void>(repository) {
139
140 @Override
141 public Void doExec(Session session) throws RepositoryException {
142 session = MgnlContext.getJCRSession(session.getWorkspace().getName());
143 if (session.nodeExists(path)) {
144 try {
145 final Node node = session.getNode(path);
146 onRegister(node);
147 } catch (RepositoryException e) {
148 log.error("Can't read configuration for {} from [{}:{}], will return null.", type, repository, path, e);
149 }
150 } else {
151 log.debug("{} does not exist, will return a default implementation for {}.", path, type);
152 instantiateDefault();
153 }
154 return null;
155 }
156
157 @Override
158 public String toString() {
159 return " load repository [" + repository + "] path [" + path + "].";
160 }
161 });
162 }
163
164 protected void instantiateDefault() {
165 if (Classes.isConcrete(type)) {
166 log.info("{} does not exist, will return a new instance of {}.", path, type);
167 final ClassFactory classFactory = Classes.getClassFactory();
168 this.observedObject = classFactory.newInstance(type);
169 } else {
170 log.warn("{} does not exist, default implementation for {} is unknown, will return null.", path, type);
171 }
172 }
173
174
175
176
177 @Deprecated
178 protected boolean isConcrete(Class<?> clazz) {
179 return Classes.isConcrete(clazz);
180 }
181
182 protected void onRegister(Node node) {
183 try {
184 final T instance = transformNode(node);
185
186 if (this.observedObject != null) {
187 log.info("Re-loaded {} from {}", type.getName(), node);
188 } else {
189 log.debug("Loading {} from {}", type.getName(), node);
190 }
191 this.observedObject = instance;
192
193 } catch (Exception e) {
194 log.error("Can't transform [{}:{}] to {}", repository, path, type, e);
195 }
196 }
197
198 protected T transformNode(Node node) throws Node2BeanException, RepositoryException {
199 return (T) Components.getComponent(Node2BeanProcessor.class).toBean(node, true, getNode2BeanTransformer(), componentProvider);
200 }
201
202 protected Node2BeanTransformer getNode2BeanTransformer() {
203 return new Node2BeanTransformerImpl();
204 }
205
206 protected Class<T> getComponentType() {
207 return type;
208 }
209
210
211
212
213
214
215
216
217 @Deprecated
218 public T getObservedObject() {
219 return this.observedObject;
220 }
221
222 protected void setObservedObject(final T observedObject) {
223 this.observedObject = observedObject;
224 }
225
226 @Override
227 public String toString() {
228 return super.toString() + ":" + type + "(Observing: " + repository + ":" + path + ")";
229 }
230 }