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.cms.i18n;
35
36 import info.magnolia.cms.util.ObservationUtil;
37 import info.magnolia.context.SystemContext;
38 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
39 import info.magnolia.jcr.node2bean.TransformationState;
40 import info.magnolia.jcr.node2bean.TypeDescriptor;
41 import info.magnolia.jcr.node2bean.TypeMapping;
42 import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
43 import info.magnolia.jcr.util.NodeTypes;
44 import info.magnolia.jcr.util.PropertyUtil;
45 import info.magnolia.objectfactory.ComponentProvider;
46 import info.magnolia.objectfactory.Components;
47 import info.magnolia.repository.RepositoryConstants;
48
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.Iterator;
54 import java.util.LinkedHashMap;
55 import java.util.Locale;
56 import java.util.Map;
57
58 import javax.inject.Inject;
59 import javax.inject.Singleton;
60 import javax.jcr.Node;
61 import javax.jcr.Session;
62 import javax.jcr.observation.EventIterator;
63 import javax.jcr.observation.EventListener;
64
65 import org.apache.commons.collections4.Transformer;
66 import org.apache.commons.collections4.map.LazyMap;
67 import org.apache.commons.lang3.StringUtils;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75 @Singleton
76 public class DefaultMessagesManager extends MessagesManager {
77 private final static Logger log = LoggerFactory.getLogger(DefaultMessagesManager.class);
78
79
80
81
82 private Locale applicationLocale;
83
84
85
86
87 private final Collection<Locale> availableLocales = new ArrayList<>();
88
89
90
91
92 private Map messages;
93
94 private String defaultBasename = DEFAULT_BASENAME;
95
96 private final Node2BeanProcessor nodeToBean;
97 private final SystemContext systemContext;
98
99 @Inject
100 public DefaultMessagesManager(Node2BeanProcessor nodeToBean, SystemContext systemContext) {
101 this.nodeToBean = nodeToBean;
102 this.systemContext = systemContext;
103
104
105 setDefaultLocale(FALLBACK_LOCALE);
106
107 initMap();
108 }
109
110
111
112
113 @Deprecated
114 public DefaultMessagesManager() {
115 this(Components.getComponent(Node2BeanProcessor.class), Components.getComponent(SystemContext.class));
116 }
117
118
119
120
121 @Deprecated
122 public DefaultMessagesManager(Node2BeanProcessor nodeToBean) {
123 this(nodeToBean, Components.getComponent(SystemContext.class));
124 }
125
126
127 void setDefaultBasename(String defaultBasename) {
128 this.defaultBasename = defaultBasename;
129 }
130
131
132
133
134
135 @Override
136 public void init() {
137 load();
138 registerEventListener();
139 }
140
141
142
143
144 protected void initMap() {
145
146 final Map map = LazyMap.lazyMap(new HashMap(), new Transformer() {
147
148
149
150
151 @Override
152 public Object transform(Object input) {
153 final MessagesID id = (MessagesID) input;
154 return newMessages(id);
155 }
156 });
157 messages = Collections.synchronizedMap(map);
158 }
159
160
161
162
163
164 protected Messages newMessages(MessagesID messagesID) {
165 Messages messages = new DefaultMessagesImpl(messagesID.basename, messagesID.locale);
166 if (!getDefaultLocale().equals(messagesID.locale)) {
167 messages = new MessagesChain(messages).chain(getMessages(messagesID.basename, getDefaultLocale()));
168 }
169 return messages;
170 }
171
172
173
174
175 protected void load() {
176 try {
177
178 final Session session = systemContext.getJCRSession(RepositoryConstants.CONFIG);
179
180 log.info("Loading i18n configuration - {}", I18N_CONFIG_PATH);
181
182
183 if (!session.nodeExists(I18N_CONFIG_PATH)) {
184 log.warn("{} does not exist yet; skipping.", I18N_CONFIG_PATH);
185 return;
186 }
187
188 final Node configNode = session.getNode(I18N_CONFIG_PATH);
189
190 setDefaultLocale(PropertyUtil.getString(configNode, FALLBACK_NODEDATA, FALLBACK_LOCALE));
191
192
193 final Node languagesNode;
194 if (configNode.hasNode(LANGUAGES_NODE_NAME)) {
195 languagesNode = configNode.getNode(LANGUAGES_NODE_NAME);
196 } else {
197 languagesNode = configNode.addNode(LANGUAGES_NODE_NAME, NodeTypes.Content.NAME);
198 }
199
200 final Map<String, LocaleDefinition> localeDefinitions = (Map<String, LocaleDefinition>) nodeToBean.setProperties(new LinkedHashMap<String, LocaleDefinition>(), languagesNode, true, new Node2BeanTransformerImpl() {
201 @Override
202 protected TypeDescriptor onResolveType(TypeMapping typeMapping, TransformationState state, TypeDescriptor resolvedType, ComponentProvider componentProvider) {
203 if (resolvedType == null && state.getLevel() == 2) {
204 return typeMapping.getTypeDescriptor(LocaleDefinition.class);
205 }
206 return resolvedType;
207 }
208 }, Components.getComponentProvider());
209
210
211 availableLocales.clear();
212
213 for (LocaleDefinition localeDefinition : localeDefinitions.values()) {
214 if (localeDefinition.isEnabled()) {
215 availableLocales.add(localeDefinition.getLocale());
216 }
217 }
218 } catch (Exception e) {
219 log.error("Failed to load i18n configuration - {}", I18N_CONFIG_PATH, e);
220 }
221 }
222
223
224
225
226 private void registerEventListener() {
227 log.info("Registering event listener for i18n");
228 ObservationUtil.registerChangeListener(RepositoryConstants.CONFIG, I18N_CONFIG_PATH, new EventListener() {
229
230 @Override
231 public void onEvent(EventIterator iterator) {
232
233 reload();
234 }
235 });
236 }
237
238
239
240
241 @Override
242 public void reload() {
243 try {
244
245 for (Iterator messagesIterator = messages.values().iterator(); messagesIterator.hasNext(); ) {
246 Messages messages = (Messages) messagesIterator.next();
247 messages.reload();
248 }
249 } catch (Exception e) {
250 log.error("Can't reload i18n messages", e);
251 }
252 initMap();
253 load();
254 }
255
256 @Override
257 public Messages getMessagesInternal(String basename, Locale locale) {
258 if (StringUtils.isEmpty(basename)) {
259 basename = defaultBasename;
260 }
261 return (Messages) messages.get(new MessagesID(basename, locale));
262 }
263
264 @Override
265 public Locale getDefaultLocale() {
266 return applicationLocale;
267 }
268
269
270
271
272
273
274
275 @Deprecated
276 public void setDefaultLocale(String defaultLocale) {
277 this.applicationLocale = new Locale(defaultLocale);
278 }
279
280 @Override
281 public Collection getAvailableLocales() {
282 return availableLocales;
283 }
284
285 public void setMessages(Map messages) {
286 this.messages = messages;
287 }
288
289
290
291
292 public static class MessagesID {
293
294 private final String basename;
295
296 private final Locale locale;
297
298 public MessagesID(String basename, Locale locale) {
299 this.basename = basename;
300 this.locale = locale;
301 }
302
303
304
305 @Override
306 public boolean equals(Object o) {
307 if (this == o) {
308 return true;
309 }
310 if (o == null || getClass() != o.getClass()) {
311 return false;
312 }
313
314 MessagesID that = (MessagesID) o;
315
316 if (basename != null ? !basename.equals(that.basename) : that.basename != null) {
317 return false;
318 }
319 if (locale != null ? !locale.equals(that.locale) : that.locale != null) {
320 return false;
321 }
322
323 return true;
324 }
325
326 @Override
327 public int hashCode() {
328 int result = basename != null ? basename.hashCode() : 0;
329 result = 31 * result + (locale != null ? locale.hashCode() : 0);
330 return result;
331 }
332
333
334
335
336
337
338 public String getBasename() {
339 return basename;
340 }
341
342
343
344
345
346
347 public Locale getLocale() {
348 return locale;
349 }
350
351 }
352 }