View Javadoc

1   /*
2    * Copyright 2006-2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.osgi.extender.internal.support;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.osgi.framework.Bundle;
22  import org.osgi.framework.BundleContext;
23  import org.osgi.framework.ServiceRegistration;
24  import org.springframework.beans.factory.DisposableBean;
25  import org.springframework.beans.factory.InitializingBean;
26  import org.springframework.beans.factory.xml.NamespaceHandlerResolver;
27  import org.springframework.osgi.util.OsgiBundleUtils;
28  import org.springframework.osgi.util.OsgiServiceUtils;
29  import org.springframework.osgi.util.OsgiStringUtils;
30  import org.springframework.util.Assert;
31  import org.xml.sax.EntityResolver;
32  
33  /**
34   * Support class that deals with namespace parsers discovered inside Spring
35   * bundles.
36   * 
37   * @author Costin Leau
38   * 
39   */
40  public class NamespaceManager implements InitializingBean, DisposableBean {
41  
42  	private static final Log log = LogFactory.getLog(NamespaceManager.class);
43  
44  	private static final String NS_HANDLER_RESOLVER_CLASS_NAME = NamespaceHandlerResolver.class.getName();
45  
46  	/**
47  	 * The set of all namespace plugins known to the extender
48  	 */
49  	private NamespacePlugins namespacePlugins;
50  
51  	/**
52  	 * ServiceRegistration object returned by OSGi when registering the
53  	 * NamespacePlugins instance as a service
54  	 */
55  	private ServiceRegistration nsResolverRegistration, enResolverRegistration = null;
56  
57  	/**
58  	 * OSGi Environment.
59  	 */
60  	private final BundleContext context;
61  
62  	private final String extenderInfo;
63  
64  	private static final String META_INF = "META-INF/";
65  
66  	private static final String SPRING_HANDLERS = "spring.handlers";
67  
68  	private static final String SPRING_SCHEMAS = "spring.schemas";
69  
70  
71  	/**
72  	 * Constructor.
73  	 * 
74  	 * @param extenderBundleContext
75  	 */
76  	public NamespaceManager(BundleContext context) {
77  		this.context = context;
78  
79  		extenderInfo = context.getBundle().getSymbolicName() + "|"
80  				+ OsgiBundleUtils.getBundleVersion(context.getBundle());
81  
82  		// detect package admin
83  		this.namespacePlugins = new NamespacePlugins();
84  	}
85  
86  	/**
87  	 * If this bundle defines handler mapping or schema mapping resources, then
88  	 * register it with the namespace plugin handler.
89  	 * 
90  	 * <p/> This method considers only the bundle space and not the class space.
91  	 * 
92  	 * @param bundle
93  	 */
94  	public void maybeAddNamespaceHandlerFor(Bundle bundle) {
95  		// Ignore system bundle
96  		if (OsgiBundleUtils.isSystemBundle(bundle)) {
97  			return;
98  		}
99  
100 		boolean hasHandlers = bundle.findEntries(META_INF, SPRING_HANDLERS, false) != null;
101 		boolean hasSchemas = bundle.findEntries(META_INF, SPRING_SCHEMAS, false) != null;
102 
103 		// if the bundle defines handlers
104 		if (hasHandlers) {
105 			// check type compatibility between the bundle's and spring-extender's spring version
106 			if (hasCompatibleNamespaceType(bundle)) {
107 				addHandler(bundle);
108 			}
109 			else {
110 				if (log.isDebugEnabled())
111 					log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle)
112 							+ "] declares namespace handlers but is not compatible with extender [" + extenderInfo
113 							+ "]; ignoring...");
114 			}
115 
116 		}
117 		else {
118 			// bundle declares only schemas, add it though the handlers might not be compatible...
119 			if (hasSchemas)
120 				addHandler(bundle);
121 		}
122 	}
123 
124 	private boolean hasCompatibleNamespaceType(Bundle bundle) {
125 		try {
126 			Class type = bundle.loadClass(NS_HANDLER_RESOLVER_CLASS_NAME);
127 			return NamespaceHandlerResolver.class.equals(type);
128 		}
129 		catch (Throwable th) {
130 			// if the interface is not wired, ignore the bundle
131 			log.warn("Bundle " + OsgiStringUtils.nullSafeNameAndSymName(bundle) + " cannot see class ["
132 					+ NS_HANDLER_RESOLVER_CLASS_NAME + "]; ignoring its namespace handlers");
133 
134 			return false;
135 		}
136 	}
137 
138 	/**
139 	 * Add this bundle to those known to provide handler or schema mappings.
140 	 * This method expects that the validity check (whatever that is) has been
141 	 * already done.
142 	 * 
143 	 * @param bundle
144 	 */
145 	protected void addHandler(Bundle bundle) {
146 		Assert.notNull(bundle);
147 		if (log.isDebugEnabled()) {
148 			log.debug("Adding namespace handler resolver for " + OsgiStringUtils.nullSafeNameAndSymName(bundle));
149 		}
150 
151 		this.namespacePlugins.addHandler(bundle);
152 	}
153 
154 	/**
155 	 * Remove this bundle from the set of those known to provide handler or
156 	 * schema mappings.
157 	 * 
158 	 * @param bundle
159 	 */
160 	public void maybeRemoveNameSpaceHandlerFor(Bundle bundle) {
161 		Assert.notNull(bundle);
162 		boolean removed = this.namespacePlugins.removeHandler(bundle);
163 		if (removed && log.isDebugEnabled()) {
164 			log.debug("Removed namespace handler resolver for " + OsgiStringUtils.nullSafeNameAndSymName(bundle));
165 		}
166 	}
167 
168 	/**
169 	 * Register the NamespacePlugins instance as an Osgi Resolver service
170 	 */
171 	private void registerResolverServices() {
172 		if (log.isDebugEnabled()) {
173 			log.debug("Registering Spring NamespaceHandlerResolver and EntityResolver...");
174 		}
175 
176 		nsResolverRegistration = context.registerService(new String[] { NamespaceHandlerResolver.class.getName() },
177 			this.namespacePlugins, null);
178 
179 		enResolverRegistration = context.registerService(new String[] { EntityResolver.class.getName() },
180 			this.namespacePlugins, null);
181 
182 	}
183 
184 	/**
185 	 * Unregister the NamespaceHandler and EntityResolver service
186 	 */
187 	private void unregisterResolverService() {
188 
189 		boolean result = OsgiServiceUtils.unregisterService(nsResolverRegistration);
190 		result = result || OsgiServiceUtils.unregisterService(enResolverRegistration);
191 
192 		if (result) {
193 			if (log.isDebugEnabled())
194 				log.debug("Unregistering Spring NamespaceHandler and EntityResolver service");
195 		}
196 
197 		this.nsResolverRegistration = null;
198 		this.enResolverRegistration = null;
199 	}
200 
201 	/**
202 	 * @return Returns the namespacePlugins.
203 	 */
204 	public NamespacePlugins getNamespacePlugins() {
205 		return namespacePlugins;
206 	}
207 
208 	//
209 	// Lifecycle methods
210 	//
211 
212 	public void afterPropertiesSet() {
213 		registerResolverServices();
214 	}
215 
216 	public void destroy() {
217 		unregisterResolverService();
218 		this.namespacePlugins.destroy();
219 		this.namespacePlugins = null;
220 	}
221 
222 }