1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.osgi.extender.support.internal;
18
19 import java.util.Dictionary;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.osgi.framework.Bundle;
24 import org.osgi.framework.Version;
25 import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;
26 import org.springframework.util.Assert;
27 import org.springframework.util.ObjectUtils;
28 import org.springframework.util.StringUtils;
29
30 /**
31 * Utility class for dealing with the extender configuration and OSGi bundle
32 * manifest headers.
33 *
34 * Defines Spring/OSGi constants and methods for configuring Spring application
35 * context.
36 *
37 * @author Costin Leau
38 *
39 */
40 public abstract class ConfigUtils {
41
42 private static final Log log = LogFactory.getLog(ConfigUtils.class);
43
44 public static final String EXTENDER_VERSION = "SpringExtender-Version";
45
46 private static final String LEFT_CLOSED_INTERVAL = "[";
47
48 private static final String LEFT_OPEN_INTERVAL = "(";
49
50 private static final String RIGHT_CLOSED_INTERVAL = "]";
51
52 private static final String RIGHT_OPEN_INTERVAL = ")";
53
54 private static final String COMMA = ",";
55
56 public static final String CONFIG_WILDCARD = "*";
57
58 /**
59 * Manifest entry name for configuring Spring application context.
60 */
61 public static final String SPRING_CONTEXT_HEADER = "Spring-Context";
62
63 /**
64 * Directive for publishing Spring application context as a service.
65 */
66 public static final String DIRECTIVE_PUBLISH_CONTEXT = "publish-context";
67
68 /**
69 * Directive for indicating wait-for time when satisfying mandatory
70 * dependencies defined in seconds
71 */
72 public static final String DIRECTIVE_TIMEOUT = "timeout";
73
74 public static final String DIRECTIVE_TIMEOUT_VALUE_NONE = "none";
75
76 /**
77 * Create asynchronously directive.
78 */
79 public static final String DIRECTIVE_CREATE_ASYNCHRONOUSLY = "create-asynchronously";
80
81 /**
82 * Wait for dependencies or directly start the context.
83 */
84 public static final String DIRECTIVE_WAIT_FOR_DEPS = "wait-for-dependencies";
85
86 /**
87 * {@link #DIRECTIVE_WAIT_FOR_DEPS} default.
88 */
89 public static final boolean DIRECTIVE_WAIT_FOR_DEPS_DEFAULT = true;
90
91 public static final String EQUALS = ":=";
92
93 /**
94 * Token used for separating directives inside a header.
95 */
96 public static final String DIRECTIVE_SEPARATOR = ";";
97
98 public static final String CONTEXT_LOCATION_SEPARATOR = ",";
99
100 public static final boolean DIRECTIVE_PUBLISH_CONTEXT_DEFAULT = true;
101
102 public static final boolean DIRECTIVE_CREATE_ASYNCHRONOUSLY_DEFAULT = true;
103
104 public static final long DIRECTIVE_TIMEOUT_DEFAULT = 5 * 60;
105
106 public static final long DIRECTIVE_NO_TIMEOUT = -2L;
107
108
109
110
111 public static boolean matchExtenderVersionRange(Bundle bundle, Version versionToMatch) {
112 Assert.notNull(bundle);
113
114 String range = (String) bundle.getHeaders().get(EXTENDER_VERSION);
115
116 boolean trace = log.isTraceEnabled();
117
118
119 if (!StringUtils.hasText(range))
120 return true;
121
122 if (trace)
123 log.trace("discovered " + EXTENDER_VERSION + " header w/ value=" + range);
124
125
126 range = StringUtils.trimWhitespace(range);
127
128
129 int commaNr = StringUtils.countOccurrencesOf(range, COMMA);
130
131
132 if (commaNr == 0) {
133 Version version = Version.parseVersion(range);
134
135 return versionToMatch.equals(version);
136 }
137
138 if (commaNr == 1) {
139
140
141 if (!((range.startsWith(LEFT_CLOSED_INTERVAL) || range.startsWith(LEFT_OPEN_INTERVAL)) && (range.endsWith(RIGHT_CLOSED_INTERVAL) || range.endsWith(RIGHT_OPEN_INTERVAL)))) {
142 throw new IllegalArgumentException("range [" + range + "] is invalid");
143 }
144
145 boolean equalMin = range.startsWith(LEFT_CLOSED_INTERVAL);
146 boolean equalMax = range.endsWith(RIGHT_CLOSED_INTERVAL);
147
148
149 range = range.substring(1, range.length() - 1);
150
151
152 String[] pieces = StringUtils.split(range, COMMA);
153
154 if (trace)
155 log.trace("discovered low/high versions : " + ObjectUtils.nullSafeToString(pieces));
156
157 Version minVer = Version.parseVersion(pieces[0]);
158 Version maxVer = Version.parseVersion(pieces[1]);
159
160 if (trace)
161 log.trace("comparing version " + versionToMatch + " w/ min=" + minVer + " and max=" + maxVer);
162
163 boolean result = true;
164
165 int compareMin = versionToMatch.compareTo(minVer);
166
167 if (equalMin)
168 result = (result && (compareMin >= 0));
169 else
170 result = (result && (compareMin > 0));
171
172 int compareMax = versionToMatch.compareTo(maxVer);
173
174 if (equalMax)
175 result = (result && (compareMax <= 0));
176 else
177 result = (result && (compareMax < 0));
178
179 return result;
180 }
181
182
183
184 throw new IllegalArgumentException("range [" + range + "] is invalid");
185 }
186
187 /**
188 * Return the {@value #SPRING_CONTEXT_HEADER} if present from the given
189 * dictionary.
190 *
191 * @param headers
192 * @return
193 */
194 public static String getSpringContextHeader(Dictionary headers) {
195 Object header = null;
196 if (headers != null)
197 header = headers.get(SPRING_CONTEXT_HEADER);
198 return (header != null ? header.toString().trim() : null);
199 }
200
201 /**
202 * Return the directive value as a String. If the directive does not exist
203 * or is invalid (wrong format) a null string will be returned.
204 *
205 * @param header
206 * @param directive
207 * @return
208 */
209 public static String getDirectiveValue(String header, String directive) {
210 Assert.notNull(header, "not-null header required");
211 Assert.notNull(directive, "not-null directive required");
212 String[] directives = StringUtils.tokenizeToStringArray(header, DIRECTIVE_SEPARATOR);
213
214 for (int i = 0; i < directives.length; i++) {
215 String[] splittedDirective = StringUtils.delimitedListToStringArray(directives[i].trim(), EQUALS);
216 if (splittedDirective.length == 2 && splittedDirective[0].equals(directive))
217 return splittedDirective[1];
218 }
219
220 return null;
221 }
222
223 /**
224 * Shortuct method to retrieve directive values. Used internally by the
225 * dedicated getXXX.
226 *
227 * @param directiveName
228 * @return
229 */
230 private static String getDirectiveValue(Dictionary headers, String directiveName) {
231 String header = getSpringContextHeader(headers);
232 if (header != null) {
233 String directive = getDirectiveValue(header, directiveName);
234 if (directive != null)
235 return directive;
236 }
237 return null;
238 }
239
240 /**
241 * Shortcut for finding the boolean value for
242 * {@link #DIRECTIVE_PUBLISH_CONTEXT} directive using the given headers.
243 * Assumes the headers belong to a Spring powered bundle.
244 *
245 * @param headers
246 * @return
247 */
248 public static boolean getPublishContext(Dictionary headers) {
249 String value = getDirectiveValue(headers, DIRECTIVE_PUBLISH_CONTEXT);
250 return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_PUBLISH_CONTEXT_DEFAULT);
251 }
252
253 /**
254 * Shortcut for finding the boolean value for
255 * {@link #DIRECTIVE_CREATE_ASYNCHRONOUSLY} directive using the given
256 * headers.
257 *
258 * Assumes the headers belong to a Spring powered bundle.
259 *
260 * @param headers
261 * @return
262 */
263 public static boolean getCreateAsync(Dictionary headers) {
264 String value = getDirectiveValue(headers, DIRECTIVE_CREATE_ASYNCHRONOUSLY);
265 return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_CREATE_ASYNCHRONOUSLY_DEFAULT);
266 }
267
268 /**
269 * Shortcut for finding the boolean value for {@link #DIRECTIVE_TIMEOUT}
270 * directive using the given headers.
271 *
272 * Assumes the headers belong to a Spring powered bundle. Returns the
273 * timeout (in seconds) for which the application context should wait to
274 * have its dependencies satisfied.
275 *
276 * @param headers
277 * @return
278 */
279 public static long getTimeOut(Dictionary headers) {
280 String value = getDirectiveValue(headers, DIRECTIVE_TIMEOUT);
281
282 if (value != null) {
283 if (DIRECTIVE_TIMEOUT_VALUE_NONE.equalsIgnoreCase(value)) {
284 return DIRECTIVE_NO_TIMEOUT;
285 }
286 return Long.valueOf(value).longValue();
287 }
288
289 return DIRECTIVE_TIMEOUT_DEFAULT;
290 }
291
292 /**
293 * Shortcut for finding the boolean value for
294 * {@link #DIRECTIVE_WAIT_FOR_DEPS} directive using the given headers.
295 * Assumes the headers belong to a Spring powered bundle.
296 *
297 * @param headers
298 * @return
299 */
300 public static boolean getWaitForDependencies(Dictionary headers) {
301 String value = getDirectiveValue(headers, DIRECTIVE_WAIT_FOR_DEPS);
302
303 return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_WAIT_FOR_DEPS_DEFAULT);
304 }
305
306 /**
307 * Returns the location headers (if any) specified by the Spring-Context
308 * header (if available). The returned Strings can be sent to a
309 * {@link org.springframework.core.io.ResourceLoader} for loading the
310 * configurations.
311 *
312 * @param headers bundle headers
313 * @return array of locations specified (if any)
314 */
315 public static String[] getHeaderLocations(Dictionary headers) {
316 String header = getSpringContextHeader(headers);
317
318 String[] ctxEntries;
319 if (StringUtils.hasText(header) && !(';' == header.charAt(0))) {
320
321 String locations = StringUtils.tokenizeToStringArray(header, DIRECTIVE_SEPARATOR)[0];
322
323 ctxEntries = StringUtils.tokenizeToStringArray(locations, CONTEXT_LOCATION_SEPARATOR);
324
325
326 for (int i = 0; i < ctxEntries.length; i++) {
327 if (CONFIG_WILDCARD.equals(ctxEntries[i]))
328 ctxEntries[i] = OsgiBundleXmlApplicationContext.DEFAULT_CONFIG_LOCATION;
329 }
330 }
331 else {
332 ctxEntries = new String[0];
333 }
334
335 return ctxEntries;
336 }
337 }