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";