You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by ld...@apache.org on 2011/01/10 17:03:18 UTC
svn commit: r1057253 - in /karaf/trunk/features/core/src:
main/java/org/apache/karaf/features/
main/java/org/apache/karaf/features/internal/ main/resources/org/
main/resources/org/apache/ main/resources/org/apache/karaf/
main/resources/org/apache/karaf...
Author: ldywicki
Date: Mon Jan 10 16:03:17 2011
New Revision: 1057253
URL: http://svn.apache.org/viewvc?rev=1057253&view=rev
Log:
KARAF-53 XML Schema for Karaf features and schema validation in FeaturesService
Added:
karaf/trunk/features/core/src/main/resources/org/
karaf/trunk/features/core/src/main/resources/org/apache/
karaf/trunk/features/core/src/main/resources/org/apache/karaf/
karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/
karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.0.0.xsd
Modified:
karaf/trunk/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
Modified: karaf/trunk/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java?rev=1057253&r1=1057252&r2=1057253&view=diff
==============================================================================
--- karaf/trunk/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java (original)
+++ karaf/trunk/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java Mon Jan 10 16:03:17 2011
@@ -33,6 +33,14 @@ public interface FeaturesService {
Verbose
}
+ /**
+ * Validate repository contents.
+ *
+ * @param uri Repository uri.
+ * @throws Exception When validation fails.
+ */
+ void validateRepository(URI uri) throws Exception;
+
void addRepository(URI url) throws Exception;
void removeRepository(URI url);
Modified: karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java?rev=1057253&r1=1057252&r2=1057253&view=diff
==============================================================================
--- karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java (original)
+++ karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java Mon Jan 10 16:03:17 2011
@@ -16,17 +16,12 @@
*/
package org.apache.karaf.features.internal;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.net.URLConnection;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.jar.JarInputStream;
@@ -64,6 +59,16 @@ import org.osgi.util.tracker.ServiceTrac
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.XMLConstants;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Schema;
+import javax.xml.validation.Validator;
+
+import org.w3c.dom.Document;
+
import static java.lang.String.format;
/**
@@ -173,6 +178,39 @@ public class FeaturesServiceImpl impleme
this.boot = boot;
}
+ /**
+ * Validate repository.
+ */
+ public void validateRepository(URI uri) throws Exception {
+ URLConnection conn = uri.toURL().openConnection();
+ conn.setDefaultUseCaches(false);
+
+ InputStream stream = conn.getInputStream();
+
+ // load document and check the root element for namespace declaration
+ DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
+ dFactory.setNamespaceAware(true);
+ Document doc = dFactory.newDocumentBuilder().parse(stream);
+
+ if (doc.getDocumentElement().getNamespaceURI() == null) {
+ return;
+ }
+
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ // root element has namespace - we can use schema validation
+ Schema schema = factory.newSchema(new StreamSource(getClass().getResourceAsStream(
+ "/org/apache/karaf/features/karaf-features-1.0.0.xsd")));
+
+ // create schema by reading it from an XSD file:
+ Validator validator = schema.newValidator();
+
+ try {
+ validator.validate(new DOMSource(doc));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Unable to validate " + uri, e);
+ }
+ }
+
public void addRepository(URI uri) throws Exception {
if (!repositories.containsKey(uri)) {
internalAddRepository(uri);
@@ -181,7 +219,8 @@ public class FeaturesServiceImpl impleme
}
protected RepositoryImpl internalAddRepository(URI uri) throws Exception {
- RepositoryImpl repo = null;
+ validateRepository(uri);
+ RepositoryImpl repo = null;
repo = new RepositoryImpl(uri);
repositories.put(uri, repo);
repo.load();
Added: karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.0.0.xsd
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.0.0.xsd?rev=1057253&view=auto
==============================================================================
--- karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.0.0.xsd (added)
+++ karaf/trunk/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.0.0.xsd Mon Jan 10 16:03:17 2011
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xs:schema elementFormDefault="qualified"
+ targetNamespace="http://karaf.apache.org/xmlns/features/v1.0.0"
+ xmlns:tns="http://karaf.apache.org/xmlns/features/v1.0.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Karaf features mechanism. For documentation please visit
+<a href="http://karaf.apache.org/">Karaf website</a>.
+ ]]></xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType name="featuresRoot">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Root element of Feature definition. It contains optional attribute which allow
+name of repository. This name will be used in shell to display source repository
+of given feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="repository" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional repositories where dependencies are stored.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="feature" type="tns:feature" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Feature definition.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" />
+ </xs:complexType>
+
+ <xs:complexType name="feature">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Definition of the Feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <!-- Tags with info -->
+ <xs:element name="description" type="xs:string">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Short description displayed in features:list command results.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="details" minOccurs="0" type="xs:string">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Long info displayed in features:info command result.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="config" type="tns:config" />
+ <xs:element name="configfile" type="tns:configFile" />
+ <xs:element name="feature" type="tns:dependency" />
+ <xs:element name="bundle" type="tns:bundle" />
+ </xs:choice>
+ <xs:attribute name="name" type="tns:featureName" use="required" />
+ <xs:attribute name="version" type="xs:string" default="0.0.0" />
+ <xs:attribute name="resolver" type="tns:resolver">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Karaf allow using dynamic resolvers.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:complexType name="bundle">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Deployable element to install.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:anyURI">
+ <xs:attribute name="start-level" type="xs:int">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Bundle start level set to OSGi framework.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="start" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+This switch allow you to leave bundle in resolved state.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="dependency" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Mark bundle as dependency for resolver.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="dependency">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Dependency of feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="tns:featureName">
+ <xs:attribute name="version" type="xs:string" default="0.0.0" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="config">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Configuration entries which should be created during feature installation. This
+configuration may be used with OSGi Configuration Admin.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="configFile">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional configuration files which should be created during feature installation.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="finalname" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Name of file name where given deployable element should be stored.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:simpleType name="featureName">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Feature name should be non empty string.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="resolver">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Resolver to use. Karaf will look for OSGi servie which have following properties:
+objectClass: org.apache.karaf.features.Resolver
+name: the value
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="features" type="tns:featuresRoot" />
+
+</xs:schema>
\ No newline at end of file
Modified: karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java?rev=1057253&r1=1057252&r2=1057253&view=diff
==============================================================================
--- karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java (original)
+++ karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java Mon Jan 10 16:03:17 2011
@@ -43,7 +43,6 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.service.log.LogService;
import org.osgi.service.packageadmin.PackageAdmin;
-import org.slf4j.Logger;
import static org.easymock.EasyMock.*;
@@ -61,7 +60,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\">");
pw.println(" <bundle>" + name + "</bundle>");
pw.println(" </feature>");
@@ -128,7 +127,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <bundle>" + name + "</bundle>");
pw.println(" </feature>");
@@ -217,7 +216,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <bundle>" + name + "</bundle>");
pw.println(" </feature>");
@@ -257,7 +256,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <bundle>" + name + "</bundle>");
pw.println(" </feature>");
@@ -349,7 +348,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <feature version=\"0.1\">f2</feature>");
pw.println(" <bundle>" + name + "</bundle>");
@@ -419,7 +418,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <feature version=\"0.1\">f2</feature>");
pw.println(" </feature>");
@@ -454,7 +453,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <feature>f2</feature>");
pw.println(" </feature>");
@@ -488,7 +487,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <feature version=\"[0.1,0.3)\">f2</feature>");
pw.println(" </feature>");
@@ -522,7 +521,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name=\"f1\" version=\"0.1\">");
pw.println(" <feature version=\"[0.1,0.3)\">f2</feature>");
pw.println(" </feature>");
@@ -605,7 +604,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name='f1'>");
pw.println(" <bundle>" + bundle1 + "</bundle>");
pw.println(" <bundle>" + "zfs:unknown" + "</bundle>");
@@ -657,7 +656,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name='f1'>");
pw.println(" <bundle>" + bundle1 + "</bundle>");
pw.println(" <bundle>" + "zfs:unknown" + "</bundle>");
@@ -710,7 +709,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name='f1'>");
pw.println(" <bundle>" + bundle1 + "</bundle>");
pw.println(" <bundle>" + "zfs:unknown" + "</bundle>");
@@ -767,7 +766,7 @@ public class FeaturesServiceTest extends
File tmp = File.createTempFile("smx", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
- pw.println("<features>");
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
pw.println(" <feature name='f1'>");
pw.println(" <bundle>" + bundle1 + "</bundle>");
pw.println(" <bundle>" + "zfs:unknown" + "</bundle>");
@@ -892,6 +891,59 @@ public class FeaturesServiceTest extends
// verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle1, installedBundle2);
}
+ /**
+ * This test checks schema validation of submited uri.
+ */
+ public void testSchemaValidation() throws Exception {
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features xmlns=\"http://karaf.apache.org/xmlns/features/v1.0.0\">");
+ pw.println(" <featur>");
+ pw.println(" <bundle>somebundle</bundle>");
+ pw.println(" </featur>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+
+ expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setBundleContext(bundleContext);
+ try {
+ svc.addRepository(uri);
+ fail("exception expected");
+ } catch (Exception e) {
+ assertTrue(e.getMessage().contains("Unable to validate"));
+ }
+ }
+
+ /**
+ * This test checks feature service behavior with old, non namespaced descriptor.
+ */
+ public void testNoSchemaValidation() throws Exception {
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <featur>");
+ pw.println(" <bundle>anotherBundle</bundle>");
+ pw.println(" </featur>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
+ replay(bundleContext);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setBundleContext(bundleContext);
+ svc.addRepository(uri);
+ }
+
private String getJarUrl(Class cl) {
String name = cl.getName();
name = name.replace(".", "/") + ".class";