blob: 26104faa7a6a0dce3af55f562a3b9ee0a401d3f2 [file] [log] [blame]
Pierre De Ropa0204f52010-03-06 22:23:57 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.impl.metatype;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.Dictionary;
26import java.util.Enumeration;
27import java.util.HashMap;
28import java.util.List;
29import java.util.Locale;
30import java.util.Map;
31import java.util.Properties;
32import java.util.StringTokenizer;
33import java.util.TreeSet;
34
35import org.apache.felix.dm.dependencies.PropertyMetaData;
36import org.apache.felix.dm.impl.Logger;
Pierre De Rop87877a52010-04-25 22:22:12 +000037import org.osgi.framework.BundleContext;
Pierre De Ropa0204f52010-03-06 22:23:57 +000038import org.osgi.framework.Constants;
39import org.osgi.service.cm.ConfigurationException;
40import org.osgi.service.cm.ManagedService;
Pierre De Rop87877a52010-04-25 22:22:12 +000041import org.osgi.service.cm.ManagedServiceFactory;
Pierre De Ropa0204f52010-03-06 22:23:57 +000042import org.osgi.service.log.LogService;
43import org.osgi.service.metatype.MetaTypeProvider;
44import org.osgi.service.metatype.ObjectClassDefinition;
45
46/**
47 * When a ConfigurationDepdendency is configured with properties metadata, we provide
48 * a specific ManagedService which also implements the MetaTypeProvider interface. This interface
49 * allows the MetaTypeService to retrieve our properties metadata, which will then be handled by webconsole.
50 */
Pierre De Rop87877a52010-04-25 22:22:12 +000051public class MetaTypeProviderImpl implements MetaTypeProvider, ManagedService, ManagedServiceFactory
Pierre De Ropa0204f52010-03-06 22:23:57 +000052{
Pierre De Rop87877a52010-04-25 22:22:12 +000053 private ManagedService m_managedServiceDelegate;
54 private ManagedServiceFactory m_managedServiceFactoryDelegate;
Pierre De Ropa0204f52010-03-06 22:23:57 +000055 private List m_propertiesMetaData = new ArrayList();
56 private String m_description;
57 private String m_heading;
58 private String m_localization;
59 private Map m_localesProperties = new HashMap();
60 private Logger m_logger;
Pierre De Rop87877a52010-04-25 22:22:12 +000061 private BundleContext m_bctx;
62 private String m_pid;
Pierre De Ropa0204f52010-03-06 22:23:57 +000063
Pierre De Rop87877a52010-04-25 22:22:12 +000064 public MetaTypeProviderImpl(String pid, BundleContext ctx, Logger logger, ManagedService msDelegate, ManagedServiceFactory msfDelegate)
Pierre De Ropa0204f52010-03-06 22:23:57 +000065 {
Pierre De Rop87877a52010-04-25 22:22:12 +000066 m_pid = pid;
67 m_bctx = ctx;
68 m_logger = logger;
69 m_managedServiceDelegate = msDelegate;
70 m_managedServiceFactoryDelegate = msfDelegate;
Pierre De Ropa0204f52010-03-06 22:23:57 +000071 // Set the default localization file base name (see core specification, in section Localization on page 68).
72 // By default, this file can be stored in OSGI-INF/l10n/bundle.properties (and corresponding localized version
73 // in OSGI-INF/l10n/bundle_en_GB_welsh.properties, OSGI-INF/l10n/bundle_en_GB.properties, etc ...
74 // This default localization property file name can be overriden using the PropertyMetaData.setLocalization method.
Pierre De Rop87877a52010-04-25 22:22:12 +000075 m_localization = (String) m_bctx.getBundle().getHeaders().get(
Pierre De Ropa0204f52010-03-06 22:23:57 +000076 Constants.BUNDLE_LOCALIZATION);
77 if (m_localization == null)
78 {
79 m_localization = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
80 }
81 }
82
83 /**
84 * Registers the metatype information of a given configuration property
85 * @param property
86 */
87 public void add(PropertyMetaData property)
88 {
89 m_propertiesMetaData.add(property);
90 }
91
92 /**
93 * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
94 * @return A human readable description of the PID this annotation is associated with (may be localized)
95 */
96 public void setDescription(String description)
97 {
98 m_description = description;
99 }
100
101 /**
102 * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
103 * @return The label used to display the tab name where the properties are displayed (may be localized)
104 */
105 public void setName(String heading)
106 {
107 m_heading = heading;
108 }
109
110 /**
111 * Points to the basename of the Properties file that can localize the Meta Type informations.
112 * By default, (e.g. <code>setLocalization("person")</code> would match person_du_NL.properties in the root bundle directory.
113 * The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
114 * be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
115 */
116 public void setLocalization(String path)
117 {
118 if (path.endsWith(".properties"))
119 {
120 throw new IllegalArgumentException(
121 "path must point to the base name of the propertie file, "
122 + "excluding local suffixes. For example: "
123 + "foo/bar/person is valid and matches the property file \"foo/bar/person_bundle_en_GB_welsh.properties\"");
124 }
125 m_localization = path.startsWith("/") ? path.substring(1) : path;
126 }
127
128 // --------------- MetaTypeProvider interface -------------------------------------------------
129
130 /**
131 * Returns all the Locales our bundle is containing. For instance, if our bundle contains the following localization files:
132 * OSGI-INF/l10n/bundle_en_GB_welsh.properties and OSGI-INF/l10n/bundle_en_GB.properties, then this method will return
133 * "en_GB", "en_GB_welsh" ...
134 * @return the list of Locale supported by our bundle.
135 */
136 public String[] getLocales()
137 {
138 int lastSlash = m_localization.lastIndexOf("/");
139 String path = (lastSlash == -1) ? "/" : ("/" + m_localization.substring(0, lastSlash - 1));
140 String base = (lastSlash == -1) ? m_localization : m_localization.substring(lastSlash + 1);
Pierre De Rop87877a52010-04-25 22:22:12 +0000141 Enumeration e = m_bctx.getBundle().findEntries(path,
Pierre De Ropa0204f52010-03-06 22:23:57 +0000142 base + "*.properties", false);
Pierre De Ropd3c2aab2010-03-07 09:52:12 +0000143 if (e == null) {
144 return null;
145 }
146
Pierre De Ropa0204f52010-03-06 22:23:57 +0000147 TreeSet set = new TreeSet();
148 while (e.hasMoreElements())
149 {
150 // We have found a locale property file in the form of "path/file[_language[_ country[_variation]].properties"
151 // And now, we have to get the "language[_country[_variation]]" part ...
152 URL url = (URL) e.nextElement();
153 String name = url.getPath();
154 name = name.substring(name.lastIndexOf("/") + 1);
155 int underscore = name.indexOf("_");
156 if (underscore != -1)
157 {
158 name = name.substring(underscore + 1, name.length() - ".properties".length());
159 }
160 if (name.length() > 0)
161 {
162 set.add(name);
163 }
164 }
165
166 String[] locales = (String[]) set.toArray(new String[set.size()]);
167 return locales.length == 0 ? null : locales;
168 }
169
170 /**
171 * Returns the ObjectClassDefinition for a given Pid/Locale.
172 */
173 public ObjectClassDefinition getObjectClassDefinition(String id, String locale)
174 {
175 try
176 {
177 // Check if the id matches our PID
Pierre De Rop87877a52010-04-25 22:22:12 +0000178 if (!id.equals(m_pid))
Pierre De Ropa0204f52010-03-06 22:23:57 +0000179 {
Pierre De Rop87877a52010-04-25 22:22:12 +0000180 m_logger.log(LogService.LOG_ERROR, "id " + id + " does not match pid " + m_pid);
Pierre De Ropa0204f52010-03-06 22:23:57 +0000181 return null;
182 }
183
184 Properties localeProperties = getLocaleProperties(locale);
Pierre De Rop87877a52010-04-25 22:22:12 +0000185 return new ObjectClassDefinitionImpl(m_pid, m_heading,
Pierre De Ropa0204f52010-03-06 22:23:57 +0000186 m_description, m_propertiesMetaData, new Resource(localeProperties));
187 }
188
189 catch (Throwable t)
190 {
Pierre De Rop87877a52010-04-25 22:22:12 +0000191 m_logger.log(
Pierre De Ropa0204f52010-03-06 22:23:57 +0000192 Logger.LOG_ERROR,
193 "Unexpected exception while geting ObjectClassDefinition for " + id + " (locale="
194 + locale + ")", t);
195 return null;
196 }
197 }
198
199 /**
200 * We also implements the ManagedService and we just delegates the configuration handling to
201 * our associated ConfigurationDependency.
202 */
203 public void updated(Dictionary properties) throws ConfigurationException
204 {
Pierre De Rop87877a52010-04-25 22:22:12 +0000205 m_managedServiceDelegate.updated(properties);
Pierre De Ropa0204f52010-03-06 22:23:57 +0000206 }
207
208 /**
209 * Gets the properties for a given Locale.
210 * @param locale
211 * @return
212 * @throws IOException
213 */
214 private synchronized Properties getLocaleProperties(String locale) throws IOException
215 {
216 locale = locale == null ? Locale.getDefault().toString() : locale;
217 Properties properties = (Properties) m_localesProperties.get(locale);
218 if (properties == null)
219 {
220 properties = new Properties();
Pierre De Rop87877a52010-04-25 22:22:12 +0000221 URL url = m_bctx.getBundle().getEntry(
Pierre De Ropa0204f52010-03-06 22:23:57 +0000222 m_localization + ".properties");
223 if (url != null)
224 {
225 loadLocale(properties, url);
226 }
227
228 String path = m_localization;
229 StringTokenizer tok = new StringTokenizer(locale, "_");
230 while (tok.hasMoreTokens())
231 {
232 path += "_" + tok.nextToken();
Pierre De Rop87877a52010-04-25 22:22:12 +0000233 url = m_bctx.getBundle().getEntry(path + ".properties");
Pierre De Ropa0204f52010-03-06 22:23:57 +0000234 if (url != null)
235 {
236 properties = new Properties(properties);
237 loadLocale(properties, url);
238 }
239 }
240
241 m_localesProperties.put(locale, properties);
242 }
243
244 return properties;
245 }
246
247 /**
248 * Loads a Locale Properties file.
249 * @param properties
250 * @param url
251 * @throws IOException
252 */
253 private void loadLocale(Properties properties, URL url) throws IOException
254 {
255 InputStream in = null;
256 try
257 {
258 in = url.openStream();
259 properties.load(in);
260 }
261 finally
262 {
263 if (in != null)
264 {
265 try
266 {
267 in.close();
268 }
269 catch (IOException ignored)
270 {
271 }
272 }
273 }
274 }
Pierre De Rop87877a52010-04-25 22:22:12 +0000275
276 // ManagedServiceFactory implementation
277
278 public void deleted(String pid)
279 {
280 m_managedServiceFactoryDelegate.deleted(pid);
281 }
282
283 public String getName()
284 {
285 return m_pid;
286 }
287
288 public void updated(String pid, Dictionary properties) throws ConfigurationException
289 {
290 m_managedServiceFactoryDelegate.updated(pid, properties);
291 }
Pierre De Ropa0204f52010-03-06 22:23:57 +0000292}