001
014
015 package com.liferay.portal.kernel.portlet;
016
017 import com.liferay.portal.kernel.util.AggregateResourceBundle;
018 import com.liferay.portal.kernel.util.ArrayUtil;
019 import com.liferay.portal.kernel.util.CharPool;
020 import com.liferay.portal.kernel.util.LocaleUtil;
021 import com.liferay.portal.kernel.util.ResourceBundleUtil;
022 import com.liferay.portal.kernel.util.StringPool;
023 import com.liferay.portal.model.Portlet;
024 import com.liferay.registry.Filter;
025 import com.liferay.registry.Registry;
026 import com.liferay.registry.RegistryUtil;
027 import com.liferay.registry.ServiceReference;
028 import com.liferay.registry.ServiceRegistration;
029 import com.liferay.registry.ServiceTracker;
030 import com.liferay.registry.ServiceTrackerCustomizer;
031 import com.liferay.registry.collections.StringServiceRegistrationMap;
032 import com.liferay.registry.collections.StringServiceRegistrationMapImpl;
033
034 import java.io.Closeable;
035
036 import java.util.Collections;
037 import java.util.Enumeration;
038 import java.util.HashSet;
039 import java.util.Iterator;
040 import java.util.List;
041 import java.util.Map.Entry;
042 import java.util.ResourceBundle;
043 import java.util.Set;
044 import java.util.concurrent.ConcurrentHashMap;
045 import java.util.concurrent.ConcurrentMap;
046 import java.util.concurrent.CopyOnWriteArrayList;
047
048
052 public class ResourceBundleTracker implements Closeable {
053
054 public ResourceBundleTracker(ClassLoader classLoader, Portlet portlet) {
055 _classLoader = classLoader;
056 _portlet = portlet;
057
058 Registry registry = RegistryUtil.getRegistry();
059
060 Filter filter = registry.getFilter(
061 "(&(javax.portlet.name=" + portlet.getPortletId() +
062 ")(language.id=*)(objectClass=" +
063 ResourceBundle.class.getName() + "))");
064
065 _serviceTracker = registry.trackServices(
066 filter, new ResourceBundleServiceTrackerCustomizer());
067
068 _serviceTracker.open();
069 }
070
071 public void clear() {
072 Set<Entry<String, ServiceRegistration<ResourceBundle>>> set =
073 _serviceRegistrations.entrySet();
074
075 Iterator<Entry<String, ServiceRegistration<ResourceBundle>>> iterator =
076 set.iterator();
077
078 while (iterator.hasNext()) {
079 Entry<String, ServiceRegistration<ResourceBundle>> entry =
080 iterator.next();
081
082 ServiceRegistration<ResourceBundle> serviceRegistration =
083 entry.getValue();
084
085 serviceRegistration.unregister();
086
087 iterator.remove();
088 }
089 }
090
091 @Override
092 public void close() {
093 clear();
094
095 _serviceTracker.close();
096 }
097
098 public ResourceBundle getResourceBundle(String languageId) {
099 if (languageId == null) {
100 languageId = StringPool.BLANK;
101 }
102
103 ResourceBundleWrapper resourceBundleWrapper =
104 _resourceBundleWrappers.get(languageId);
105
106 if (resourceBundleWrapper == null) {
107 resourceBundleWrapper = new ResourceBundleWrapper(languageId, null);
108
109 ResourceBundleWrapper previousResourceBundleWrapper =
110 _resourceBundleWrappers.putIfAbsent(
111 languageId, resourceBundleWrapper);
112
113 if (previousResourceBundleWrapper != null) {
114 resourceBundleWrapper = previousResourceBundleWrapper;
115 }
116 }
117
118 return resourceBundleWrapper;
119 }
120
121 private final ClassLoader _classLoader;
122 private final Portlet _portlet;
123 private final ConcurrentMap<String, ResourceBundleWrapper>
124 _resourceBundleWrappers = new ConcurrentHashMap<>();
125 private final StringServiceRegistrationMap<ResourceBundle>
126 _serviceRegistrations = new StringServiceRegistrationMapImpl<>();
127 private final ServiceTracker<ResourceBundle, ResourceBundle>
128 _serviceTracker;
129
130 private class ResourceBundleServiceTrackerCustomizer
131 implements ServiceTrackerCustomizer<ResourceBundle, ResourceBundle> {
132
133 @Override
134 public ResourceBundle addingService(
135 ServiceReference<ResourceBundle> serviceReference) {
136
137 Registry registry = RegistryUtil.getRegistry();
138
139 ResourceBundle resourceBundle = registry.getService(
140 serviceReference);
141
142 String languageId = (String)serviceReference.getProperty(
143 "language.id");
144
145 ResourceBundleWrapper resourceBundleWrapper =
146 _resourceBundleWrappers.get(languageId);
147
148 if (resourceBundleWrapper == null) {
149 resourceBundleWrapper = new ResourceBundleWrapper(
150 languageId, resourceBundle);
151
152 ResourceBundleWrapper previousResourceBundleWrapper =
153 _resourceBundleWrappers.putIfAbsent(
154 languageId, resourceBundleWrapper);
155
156 if (previousResourceBundleWrapper == null) {
157 return resourceBundle;
158 }
159 else {
160 resourceBundleWrapper = previousResourceBundleWrapper;
161 }
162 }
163
164 resourceBundleWrapper._addResourceBundle(resourceBundle);
165
166 return resourceBundle;
167 }
168
169 @Override
170 public void modifiedService(
171 ServiceReference<ResourceBundle> serviceReference,
172 ResourceBundle resourceBundle) {
173
174 removedService(serviceReference, resourceBundle);
175
176 addingService(serviceReference);
177 }
178
179 @Override
180 public void removedService(
181 ServiceReference<ResourceBundle> serviceReference,
182 ResourceBundle resourceBundle) {
183
184 Registry registry = RegistryUtil.getRegistry();
185
186 registry.ungetService(serviceReference);
187
188 String languageId = (String)serviceReference.getProperty(
189 "language.id");
190
191 ResourceBundleWrapper resourceBundleWrapper =
192 _resourceBundleWrappers.get(languageId);
193
194 if (resourceBundleWrapper != null) {
195 resourceBundleWrapper._removeResourceBundle(resourceBundle);
196 }
197 }
198
199 }
200
201 private class ResourceBundleWrapper extends ResourceBundle {
202
203 @Override
204 public Enumeration<String> getKeys() {
205 ResourceBundle resourceBundle = _resourceBundle;
206
207 if (resourceBundle == null) {
208 return Collections.emptyEnumeration();
209 }
210
211 return resourceBundle.getKeys();
212 }
213
214 @Override
215 protected Object handleGetObject(String key) {
216 ResourceBundle resourceBundle = _resourceBundle;
217
218 if ((resourceBundle != null) && resourceBundle.containsKey(key)) {
219 return resourceBundle.getObject(key);
220 }
221
222 return null;
223 }
224
225 @Override
226 protected Set<String> handleKeySet() {
227 Set<String> keySet = _keySet;
228
229 if (keySet == null) {
230 ResourceBundle resourceBundle = _resourceBundle;
231
232 if (resourceBundle == null) {
233 keySet = Collections.emptySet();
234 }
235 else {
236 keySet = new HashSet<>();
237
238 Enumeration<String> enumeration = resourceBundle.getKeys();
239
240 while (enumeration.hasMoreElements()) {
241 String key = enumeration.nextElement();
242
243 if (resourceBundle.containsKey(key)) {
244 keySet.add(key);
245 }
246 }
247 }
248
249 _keySet = keySet;
250 }
251
252 return keySet;
253 }
254
255 private ResourceBundleWrapper(
256 String languageId, ResourceBundle resourceBundle) {
257
258 _resourceBundle = resourceBundle;
259
260 if (resourceBundle != null) {
261 _resourceBundles.add(resourceBundle);
262 }
263
264 if (languageId.isEmpty()) {
265 setParent(
266 ResourceBundleUtil.getBundle(
267 _portlet.getResourceBundle(),
268 LocaleUtil.fromLanguageId(languageId), _classLoader));
269 }
270 else {
271 String parentLanguageId = StringPool.BLANK;
272
273 int index = languageId.lastIndexOf(CharPool.UNDERLINE);
274
275 if (index > 0) {
276 parentLanguageId = languageId.substring(0, index);
277 }
278
279 setParent(getResourceBundle(parentLanguageId));
280 }
281 }
282
283 private void _addResourceBundle(ResourceBundle resourceBundle) {
284 _resourceBundles.add(resourceBundle);
285
286 _update();
287 }
288
289 private void _removeResourceBundle(ResourceBundle resourceBundle) {
290 if (_resourceBundles.remove(resourceBundle)) {
291 _update();
292 }
293 }
294
295 private void _update() {
296 ResourceBundle[] resourceBundles = _resourceBundles.toArray(
297 new ResourceBundle[_resourceBundles.size()]);
298
299 if (resourceBundles.length == 0) {
300 _resourceBundle = null;
301 }
302 else if (resourceBundles.length == 1) {
303 _resourceBundle = resourceBundles[0];
304 }
305 else {
306 ArrayUtil.reverse(resourceBundles);
307
308 _resourceBundle = new AggregateResourceBundle(resourceBundles);
309 }
310
311 _keySet = null;
312 }
313
314 private volatile Set<String> _keySet;
315 private volatile ResourceBundle _resourceBundle;
316 private final List<ResourceBundle> _resourceBundles =
317 new CopyOnWriteArrayList<>();
318
319 }
320
321 }