Author: gnodet
Date: Wed Apr 13 10:39:35 2016
New Revision: 1738921
URL: http://svn.apache.org/viewvc?rev=1738921&view=rev
Log:
[ARIES-1503] Another fix and small refactoring
The trick is to remember loaded resources and recognize 'bad' behaving
namespace handlers, i.e. those that always return the same url, irrespective of
the resource trying to be loaded. If a loader returns an already loaded url for
a resource, we ignore those.
In order for this to work, we also need to be smarter about identifying similar
resources. We need to cope with the fact that a schema can be imported with or
without a location for example, so we ignore the systemId when the schema is
imported. If the resource has already been loaded, quickly return the
previously loaded resource.
The blueprint container is also fixed wrt to added namespaces. The handler set
now do a copy of the schemas, and we use those to find missing namespaces.
Whenever a handler is removed, we can't easily find out which namespaces have
been dynamically added, so we simply recreate the handler set completely.
Modified:
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/namespace/NamespaceHandlerRegistryImpl.java
Modified:
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
URL:
http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java?rev=1738921&r1=1738920&r2=1738921&view=diff
==============================================================================
---
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
(original)
+++
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
Wed Apr 13 10:39:35 2016
@@ -315,7 +315,7 @@ public class BlueprintContainerImpl
{
List<String> missing = new ArrayList<String>();
List<URI> missingURIs = new ArrayList<URI>();
- for (URI ns : namespaces) {
+ for (URI ns : handlerSet.getNamespaces()) {
if (handlerSet.getNamespaceHandler(ns) == null) {
missing.add("(&(" + Constants.OBJECTCLASS +
"=" + NamespaceHandler.class.getName() + ")(" +
NamespaceHandlerRegistryImpl.NAMESPACE + "=" + ns + "))");
missingURIs.add(ns);
@@ -351,7 +351,7 @@ public class BlueprintContainerImpl
} catch (MissingNamespaceException e) {
// If we found a missing namespace when parsing
the schema,
// we remain in the current state
- namespaces.add(e.getNamespace());
+ handlerSet.getNamespaces().add(e.getNamespace());
}
break;
}
@@ -933,13 +933,13 @@ public class BlueprintContainerImpl
}
public void namespaceHandlerRegistered(URI uri) {
- if (namespaces != null && namespaces.contains(uri)) {
+ if (handlerSet != null && handlerSet.getNamespaces().contains(uri)) {
schedule();
}
}
public void namespaceHandlerUnregistered(URI uri) {
- if (namespaces != null && namespaces.contains(uri)) {
+ if (handlerSet != null && handlerSet.getNamespaces().contains(uri)) {
synchronized (scheduled) {
if (destroyed.get()) {
return;
@@ -948,6 +948,10 @@ public class BlueprintContainerImpl
resetComponentDefinitionRegistry();
cancelFutureIfPresent();
this.repository = null;
+ handlerSet.removeListener(this);
+ handlerSet.destroy();
+ handlerSet = handlers.getNamespaceHandlers(namespaces,
getBundle());
+ handlerSet.addListener(this);
state = State.WaitForNamespaceHandlers;
schedule();
}
Modified:
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/namespace/NamespaceHandlerRegistryImpl.java
URL:
http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/namespace/NamespaceHandlerRegistryImpl.java?rev=1738921&r1=1738920&r2=1738921&view=diff
==============================================================================
---
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/namespace/NamespaceHandlerRegistryImpl.java
(original)
+++
aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/namespace/NamespaceHandlerRegistryImpl.java
Wed Apr 13 10:39:35 2016
@@ -44,7 +44,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
-import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
@@ -64,6 +63,10 @@ import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
+import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
+import static javax.xml.XMLConstants.XML_NS_URI;
+import static org.apache.aries.blueprint.parser.Parser.BLUEPRINT_NAMESPACE;
+
/**
* Default implementation of the NamespaceHandlerRegistry.
*
@@ -94,7 +97,7 @@ public class NamespaceHandlerRegistryImp
// Access to this factory is synchronized on itself
private final SchemaFactory schemaFactory =
-
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
// Access to this variable is must be synchronized on itself
private final ArrayList<NamespaceHandlerSetImpl> sets =
@@ -388,7 +391,7 @@ public class NamespaceHandlerRegistryImp
public NamespaceHandlerSetImpl(Set<URI> namespaces, Bundle bundle) {
this.listeners = new CopyOnWriteArrayList<Listener>();
- this.namespaces = namespaces;
+ this.namespaces = new HashSet<URI>(namespaces);
this.bundle = bundle;
handlers = new ConcurrentHashMap<URI, NamespaceHandler>();
for (URI ns : namespaces) {
@@ -458,13 +461,155 @@ public class NamespaceHandlerRegistryImp
return schema;
}
+ private class Loader implements LSResourceResolver, AutoCloseable {
+ final List<StreamSource> sources = new ArrayList<StreamSource>();
+ final Map<String, URL> loaded = new HashMap<String, URL>();
+ final Map<String, String> namespaces = new HashMap<String,
String>();
+ @Override
+ public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
+ // Compute id
+ String id;
+ String prevNamespace = baseURI != null ?
namespaces.get(baseURI) : null;
+ if (namespaceURI != null && prevNamespace != null &&
namespaceURI.equals(prevNamespace)) {
+ // This is an include
+ id = getId(type, namespaceURI, publicId, systemId);
+ } else {
+ id = getId(type, namespaceURI, publicId, null);
+ }
+ // Check if it has already been loaded
+ if (loaded.containsKey(id)) {
+ return createLSInput(loaded.get(id), id, namespaceURI);
+ }
+ // Schema map
+ //-----------
+ // Use provided schema map to find the resource.
+ // If the schema map contains the namespaceURI, publicId or
systemId,
+ // load the corresponding resource directly from the bundle.
+ String loc = null;
+ if (namespaceURI != null) {
+ loc = schemaMap.getProperty(namespaceURI);
+ }
+ if (loc == null && publicId != null) {
+ loc = schemaMap.getProperty(publicId);
+ }
+ if (loc == null && systemId != null) {
+ loc = schemaMap.getProperty(systemId);
+ }
+ if (loc != null) {
+ URL url = bundle.getResource(loc);
+ if (url != null) {
+ return createLSInput(url, id, namespaceURI);
+ }
+ }
+ // Relative uris
+ //---------------
+ // For relative uris, don't use the namespace handlers, but
simply resolve the uri
+ // and use that one directly to load the resource.
+ String resolved = resolveIfRelative(systemId, baseURI);
+ if (resolved != null) {
+ URL url;
+ try {
+ url = new URL(resolved);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return createLSInput(url, id, namespaceURI);
+ }
+ // Only support xml schemas from now on
+ if (namespaceURI == null ||
!W3C_XML_SCHEMA_NS_URI.equals(type)) {
+ return null;
+ }
+ // We are now loading a schema, or schema part with
+ // * notNull(namespaceURI)
+ // * null(systemId) or absolute(systemId)
+ URI nsUri = URI.create(namespaceURI);
+ String rid = systemId != null ? systemId : namespaceURI;
+ NamespaceHandler h = getNamespaceHandler(nsUri);
+ // This is a resource from a known namespace
+ if (h != null) {
+ URL url = h.getSchemaLocation(rid);
+ if (isCorrectUrl(url)) {
+ return createLSInput(url, id, namespaceURI);
+ }
+ }
+ else {
+ // Ask known handlers if they have this schema
+ for (NamespaceHandler hd : handlers.values()) {
+ URL url = hd.getSchemaLocation(rid);
+ if (isCorrectUrl(url)) {
+ return createLSInput(url, id, namespaceURI);
+ }
+ }
+ // Find a compatible namespace handler
+ LOGGER.warn("Dynamically adding namespace handler {} to
bundle {}/{}",
+ nsUri, bundle.getSymbolicName(),
bundle.getVersion());
+ h = findCompatibleNamespaceHandler(nsUri);
+ if (h != null) {
+ URL url = h.getSchemaLocation(rid);
+ if (isCorrectUrl(url)) {
+ return createLSInput(url, id, namespaceURI);
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getId(String type, String namespaceURI, String
publicId, String systemId) {
+ return type + "|" + namespaceURI + "|" + publicId + "|" +
systemId;
+ }
+
+ public StreamSource use(URL resource, String id, String namespace)
throws IOException {
+ String url = resource.toExternalForm();
+ StreamSource ss = new StreamSource(resource.openStream(), url);
+ sources.add(ss);
+ loaded.put(id, resource);
+ namespaces.put(url, namespace);
+ return ss;
+ }
+
+ @Override
+ public void close() {
+ for (StreamSource source : sources) {
+ closeQuietly(source.getInputStream());
+ }
+ }
+
+ public Source[] getSources() {
+ return sources.toArray(new Source[sources.size()]);
+ }
+
+ private boolean isCorrectUrl(URL url) {
+ return url != null && !loaded.values().contains(url);
+ }
+ private String resolveIfRelative(String systemId, String baseURI) {
+ if (baseURI != null && systemId != null) {
+ URI sId = URI.create(systemId);
+ if (!sId.isAbsolute()) {
+ return URI.create(baseURI).resolve(sId).toString();
+ }
+ }
+ return null;
+ }
+ private LSInput createLSInput(URL url, String id, String
namespace) {
+ try {
+ return new SourceLSInput(use(url, id, namespace));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private Schema createSchema(Map<String, String> locations) throws
IOException, SAXException {
- final List<StreamSource> schemaSources = new
ArrayList<StreamSource>();
+ Loader loader = new Loader();
try {
- schemaSources.add(new
StreamSource(getClass().getResourceAsStream("/org/apache/aries/blueprint/blueprint.xsd")));
- schemaSources.add(new
StreamSource(getClass().getResourceAsStream("/org/apache/aries/blueprint/ext/impl/xml.xsd")));
- // Create a schema for all namespaces known at this point
- // It will speed things as it can be reused for all other
blueprint containers
+
loader.use(getClass().getResource("/org/osgi/service/blueprint/blueprint.xsd"),
+ loader.getId(W3C_XML_SCHEMA_NS_URI,
BLUEPRINT_NAMESPACE, null, null),
+ BLUEPRINT_NAMESPACE);
+
loader.use(getClass().getResource("/org/apache/aries/blueprint/ext/impl/xml.xsd"),
+ loader.getId(W3C_XML_SCHEMA_NS_URI, XML_NS_URI,
null, null),
+ XML_NS_URI);
+
+ // Create a schema for the namespaces
for (URI ns : handlers.keySet()) {
URL url =
handlers.get(ns).getSchemaLocation(ns.toString());
if (url == null && locations != null) {
@@ -476,7 +621,7 @@ public class NamespaceHandlerRegistryImp
if (url == null) {
LOGGER.warn("No URL is defined for schema " + ns + ".
This schema will not be validated");
} else {
- schemaSources.add(new StreamSource(url.openStream(),
url.toExternalForm()));
+ loader.use(url, loader.getId(W3C_XML_SCHEMA_NS_URI,
ns.toString(), null, null), ns.toString());
}
}
for (Object ns : schemaMap.values()) {
@@ -484,89 +629,18 @@ public class NamespaceHandlerRegistryImp
if (url == null) {
LOGGER.warn("No URL is defined for schema " + ns + ".
This schema will not be validated");
} else {
- schemaSources.add(new StreamSource(url.openStream(),
url.toExternalForm()));
+ loader.use(url, loader.getId(W3C_XML_SCHEMA_NS_URI,
ns.toString(), null, null), ns.toString());
}
}
- LSResourceResolver resolver = new LSResourceResolver() {
- public LSInput resolveResource(String type,
- final String namespaceURI,
- final String publicId,
- String systemId,
- String baseURI) {
- URI nsUri = namespaceURI != null ?
URI.create(namespaceURI) : null;
- // Use provided schema map to find the resource
- String loc = null;
- if (namespaceURI != null) {
- loc = schemaMap.getProperty(namespaceURI);
- }
- if (loc == null && publicId != null) {
- loc = schemaMap.getProperty(publicId);
- }
- if (loc == null && systemId != null) {
- loc = schemaMap.getProperty(systemId);
- }
- if (loc != null) {
- URL url = bundle.getResource(loc);
- if (url != null) {
- return createLSInput(url);
- }
- }
- // Support include-relative-path case
- if (baseURI != null && systemId != null &&
!systemId.matches("^[a-z][-+.0-9a-z]*:.*")) {
- URL url;
- try {
- url = new URL(new URL(baseURI), systemId);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return createLSInput(url);
- }
- if (namespaceURI != null) {
- String id = systemId != null ? systemId :
namespaceURI;
- // This is a namespace with a known handler
- NamespaceHandler h = getNamespaceHandler(nsUri);
- if (h == null) {
- // Find a compatible namespace
- LOGGER.warn("Dynamically adding namespace
handler {} to bundle {}/{}",
- nsUri, bundle.getSymbolicName(),
bundle.getVersion());
- h = findCompatibleNamespaceHandler(nsUri);
- }
- // Load from the handler
- if (h != null) {
- URL url = h.getSchemaLocation(id);
- if (url != null) {
- return createLSInput(url);
- }
- } else {
- throw new MissingNamespaceException(nsUri);
- }
- }
- return null;
- }
-
- private LSInput createLSInput(URL url) {
- try {
- String systemId = url.toExternalForm();
- final StreamSource source = new
StreamSource(url.openStream(), systemId);
- schemaSources.add(source);
- return new SourceLSInput(source);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- };
synchronized (schemaFactory) {
- schemaFactory.setResourceResolver(resolver);
- return schemaFactory.newSchema(schemaSources.toArray(new
Source[schemaSources.size()]));
+ schemaFactory.setResourceResolver(loader);
+ return schemaFactory.newSchema(loader.getSources());
}
} finally {
- for (StreamSource s : schemaSources) {
- closeQuietly(s.getInputStream());
- }
+ loader.close();
}
}
-
public void addListener(Listener listener) {
listeners.add(listener);
}