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