package test.component;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.jar.Manifest;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.osgi.framework.ServiceReference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Builder;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Jar;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Resource;
import aQute.lib.io.IO;
import aQute.lib.io.IOConstants;
import junit.framework.TestCase;
/**
* Test for use of DS components specified only by Service-Component headers.
*/
@SuppressWarnings({
"resource", "rawtypes"
})
public class ComponentTest extends TestCase {
static final int BUFFER_SIZE = IOConstants.PAGE_SIZE * 1;
static final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
static final XPathFactory xpathf = XPathFactory.newInstance();
static final XPath xpath = xpathf.newXPath();
static DocumentBuilder db;
static {
try {
dbf.setNamespaceAware(true);
db = dbf.newDocumentBuilder();
xpath.setNamespaceContext(new NamespaceContext() {
@Override
public Iterator<String> getPrefixes(String namespaceURI) {
return Arrays.asList("md", "scr").iterator();
}
@Override
public String getPrefix(String namespaceURI) {
if (namespaceURI.equals("http://www.osgi.org/xmlns/metatype/v1.1.0"))
return "md";
if (namespaceURI.equals("http://www.osgi.org/xmlns/scr/v1.1.0"))
return "scr";
return null;
}
@Override
public String getNamespaceURI(String prefix) {
if (prefix.equals("md"))
return "http://www.osgi.org/xmlns/metatype/v1.1.0";
else if (prefix.equals("scr"))
return "http://www.osgi.org/xmlns/scr/v1.1.0";
else
return null;
}
});
} catch (ParserConfigurationException e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
public static class ReferenceOrder {
void setA(ServiceReference sr) {}
void unsetA(ServiceReference sr) {}
void setZ(ServiceReference sr) {}
void unsetZ(ServiceReference sr) {}
}
/**
* 112.5.7 says refeence order is used to order binding services, so from
* headers we preserve order.
*
* @throws Exception
*/
public void testHeaderReferenceOrder() throws Exception {
Document doc = setup(
ReferenceOrder.class.getName()
+ ";version:=1.1;z=org.osgi.service.http.HttpService?;a=org.osgi.service.http.HttpService?",
ReferenceOrder.class.getName());
assertAttribute(doc, "z", "scr:component/reference[1]/@name");
assertAttribute(doc, "a", "scr:component/reference[2]/@name");
}
/**
* Test to see if we ignore scala.ScalaObject as interface
*
* @throws Exception
*/
public void testScalaObject() throws Exception {
Builder b = new Builder();
b.addClasspath(IO.getFile("jar/com.test.scala.jar"));
b.setProperty("Service-Component", "*");
b.setProperty("Export-Package", "com.test.scala.*");
Jar jar = b.build();
Manifest m = jar.getManifest();
System.err.println(Processor.join(b.getErrors()));
System.err.println(Processor.join(b.getWarnings()));
System.err.println(m.getMainAttributes().getValue("Service-Component"));
IO.copy(jar.getResource("OSGI-INF/com.test.scala.Service.xml").openInputStream(), System.err);
Document doc = doc(b, "com.test.scala.Service");
assertEquals("com.test.scala.Service", xpath.evaluate("component/implementation/@class", doc));
assertEquals("", xpath.evaluate("component/service/provide/@interface", doc));
}
/**
* Test if a reference is made to an interface implemented on a superclass.
* This is from https://github.com/bndtools/bnd/issues#issue/23
*/
public void testProvideFromSuperClass() throws Exception {
Builder b = new Builder();
b.setClasspath(new File[] {
IO.getFile("bin")
});
b.setProperty("Service-Component", "*InheritedActivator");
b.setProperty("Private-Package", "test.activator.inherits");
b.setProperty("-fixupmessages.bndannodeprecated", "Bnd DS annotations are deprecated");
b.addClasspath(IO.getFile("jar/osgi.jar"));
b.build();
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
assertEquals(0, b.getErrors().size());
assertEquals(0, b.getWarnings().size());
Manifest m = b.getJar().getManifest();
String imports = m.getMainAttributes().getValue("Import-Package");
assertTrue(imports.contains("org.osgi.framework"));
}
/**
* A non-FQN entry but we demand no annotations, should generate an error
* and no component
*/
public void testNonFQNAndNoAnnotations() throws Exception {
Builder b = new Builder();
b.setProperty("Include-Resource",
"org/osgi/impl/service/coordinator/AnnotationWithJSR14.class=jar/AnnotationWithJSR14.jclass");
b.setProperty("Service-Component", "*;" + Constants.NOANNOTATIONS + "=true");
b.setProperty("-resourceonly", "true");
Jar jar = b.build();
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
assertEquals(1, b.getErrors().size());
assertEquals(0, b.getWarnings().size());
Manifest manifest = jar.getManifest();
String component = manifest.getMainAttributes().getValue("Service-Component");
System.err.println(component);
assertNull(component);
}
public static void assertAttribute(Document doc, String value, String expr) throws XPathExpressionException {
System.err.println(expr);
String o = (String) xpath.evaluate(expr, doc, XPathConstants.STRING);
if (o == null) {
}
assertNotNull(o);
assertEquals(value, o);
}
static Document doc(Builder b, String name) throws Exception {
Jar jar = b.getJar();
Resource r = jar.getResource("OSGI-INF/" + name + ".xml");
assertNotNull(r);
Document doc = db.parse(r.openInputStream());
r.write(System.err);
return doc;
}
public void testV1_1Directives() throws Exception {
Element component = setup(
"test.activator.Activator11;factory:=blabla;immediate:=true;enabled:=false;configuration-policy:=optional;activate:=start;deactivate:=stop;modified:=modded",
"test.activator.Activator11").getDocumentElement();
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
assertEquals("blabla", component.getAttribute("factory"));
assertEquals("false", component.getAttribute("enabled"));
assertEquals("optional", component.getAttribute("configuration-policy"));
assertEquals("start", component.getAttribute("activate"));
assertEquals("stop", component.getAttribute("deactivate"));
assertEquals("modded", component.getAttribute("modified"));
}
public void testNoNamespace() throws Exception {
Element component = setup("test.activator.Activator");
assertEquals(null, component.getNamespaceURI());
}
public void testAutoNamespace() throws Exception {
Element component = setup("test.activator.Activator;activate:='start';deactivate:='stop'");
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
// activate/deactivate with BundleContext args
component = setup("test.activator.Activator2", "test.activator.Activator2").getDocumentElement();
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
// deactivate with a int reason
component = setup("test.activator.Activator3", "test.activator.Activator3").getDocumentElement();
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
// package access activate/deactivate with ComponentContext args
component = setup("test.activator.ActivatorPackage", "test.activator.ActivatorPackage").getDocumentElement();
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
// private access activate/deactivate with ComponentContext args
component = setup("test.activator.ActivatorPrivate", "test.activator.ActivatorPrivate").getDocumentElement();
assertEquals("http://www.osgi.org/xmlns/scr/v1.1.0", component.getNamespaceURI());
}
public void testCustomVersion() throws Exception {
Element component = setup("test.activator.Activator;version:=2");
assertEquals("http://www.osgi.org/xmlns/scr/v2.0.0", component.getNamespaceURI());
}
public void testCustomNamespace() throws Exception {
Element component = setup("test.activator.Activator;xmlns:='http://www.osgi.org/xmlns/xscr/v2.0.0'");
assertEquals("http://www.osgi.org/xmlns/xscr/v2.0.0", component.getNamespaceURI());
}
static Document setup(String header, String className) throws Exception {
Builder b = new Builder();
b.setProperty(Analyzer.SERVICE_COMPONENT, header);
b.setClasspath(new File[] {
IO.getFile("bin"), IO.getFile("jar/osgi.jar")
});
b.setProperty("Private-Package", "test.activator, org.osgi.service.http.*");
b.build();
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
List<String> errors = b.getErrors();
// ignore the dynamic-without-unbind error here
if (!errors.isEmpty()) {
assertEquals(1, errors.size());
assertTrue(errors.get(0).endsWith("dynamic but has no unbind method."));
}
assertEquals(0, b.getWarnings().size());
String path = "OSGI-INF/" + className + ".xml";
print(b.getJar().getResource(path), System.err);
Document doc = db.parse(new InputSource(b.getJar().getResource(path).openInputStream()));
return doc;
}
static Element setup(String header) throws Exception {
return setup(header, "test.activator.Activator").getDocumentElement();
}
private static void print(Resource resource, OutputStream out) throws Exception {
InputStream in = resource.openInputStream();
try {
byte[] buffer = new byte[BUFFER_SIZE];
int size = in.read(buffer);
while (size > 0) {
out.write(buffer, 0, size);
size = in.read(buffer);
}
out.flush();
} finally {
in.close();
}
}
/*
* public void testWildcards() throws Exception { Builder b = new Builder();
* b .setProperty(Analyzer.SERVICE_COMPONENT,
* "testresources/component/*.xml"); b.setProperty("-resourceonly", "true");
* b.setProperty("Include-Resource",
* "testresources/component=testresources/component"); Jar jar = b.build();
* System.err.println(b.getErrors()); System.err.println(b.getWarnings());
* assertEquals(0, b.getErrors().size()); assertEquals(0,
* b.getWarnings().size()); }
*/
public void testImplementation() throws Exception {
Builder b = new Builder();
b.setProperty(Analyzer.SERVICE_COMPONENT,
"silly.name;implementation:=test.activator.Activator;provide:=java.io.Serialization;servicefactory:=true");
b.setClasspath(new File[] {
IO.getFile("bin"), IO.getFile("jar/osgi.jar")
});
b.setProperty("Private-Package", "test.activator");
b.build();
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
assertEquals(0, b.getErrors().size());
assertEquals(0, b.getWarnings().size());
Jar jar = b.getJar();
Document doc = db.parse(new InputSource(jar.getResource("OSGI-INF/silly.name.xml").openInputStream()));
assertEquals("test.activator.Activator", doc.getElementsByTagName("implementation")
.item(0)
.getAttributes()
.getNamedItem("class")
.getNodeValue());
assertEquals("true", doc.getElementsByTagName("service")
.item(0)
.getAttributes()
.getNamedItem("servicefactory")
.getNodeValue());
}
/**
* Standard activator with reference to http.
*
* @throws Exception
*/
public void testProperties() throws Exception {
java.util.Properties p = new Properties();
p.put(Analyzer.EXPORT_PACKAGE, "test.activator,org.osgi.service.http");
p.put(Analyzer.IMPORT_PACKAGE, "*");
p.put(Analyzer.SERVICE_COMPONENT, "test.activator.Activator;properties:=\"a=3|4,b=1|2|3\"");
Builder b = new Builder();
b.setClasspath(new File[] {
IO.getFile("bin"), IO.getFile("jar/osgi.jar")
});
b.setProperties(p);
b.build();
assertEquals(0, b.getErrors().size());
assertEquals(0, b.getWarnings().size());
Jar jar = b.getJar();
Resource resource = jar.getResource("OSGI-INF/test.activator.Activator.xml");
IO.copy(resource.openInputStream(), System.out);
Document doc = db.parse(new InputSource(resource.openInputStream()));
NodeList l = doc.getElementsByTagName("property");
assertEquals(2, l.getLength());
boolean aset = false, bset = false;
for (int i = 0; i < 2; i++) {
Node child = l.item(i);
NamedNodeMap attributes = child.getAttributes();
Node namedItem = attributes.getNamedItem("name");
String name = namedItem.getNodeValue();
String text = child.getFirstChild().getNodeValue().trim();
if (name.equals("a")) {
aset = true;
assertEquals("3\n4", text);
}
if (name.equals("b")) {
bset = true;
assertEquals("1\n2\n3", text);
}
}
assertTrue(aset);
assertTrue(bset);
assertEquals("test.activator.Activator", doc.getElementsByTagName("implementation")
.item(0)
.getAttributes()
.getNamedItem("class")
.getNodeValue());
// assertEquals("test.activator.Activator", xp.evaluate(
// "/component/implementation/@class", doc));
// assertEquals("org.osgi.service.http.HttpService", xp.evaluate(
// "/component/reference[@name='http']/@interface", doc));
// assertEquals("setHttp", xp.evaluate(
// "/component/reference[@name='http']/@bind", doc));
// assertEquals("unsetHttp", xp.evaluate(
// "/component/reference[@name='http']/@unbind", doc));
// assertEquals("", xp.evaluate(
// "/component/reference[@name='http']/@target", doc));
}
/**
* Check if all the directives work
*
* @throws Exception
*/
public void testUnknownDirective() throws Exception {
java.util.Properties p = new Properties();
p.put(Analyzer.EXPORT_PACKAGE, "test.activator,org.osgi.service.http");
p.put(Analyzer.IMPORT_PACKAGE, "*");
p.put(Analyzer.SERVICE_COMPONENT, "test.activator.Activator;provides:=true");
Builder b = new Builder();
b.setClasspath(new File[] {
IO.getFile("bin"), IO.getFile("jar/osgi.jar")
});
b.setProperties(p);
b.build();
doc(b, "test.activator.Activator");
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
assertEquals(1, b.getErrors().size());
assertTrue(b.getErrors().get(0).indexOf("Unrecognized directive") >= 0);
assertEquals(0, b.getWarnings().size());
}
/**
* Check if all the directives work
*
* @throws Exception
*/
public void testDirectives() throws Exception {
Document doc = setup(
"test.activator.Activator;http=org.osgi.service.http.HttpService;dynamic:=http;optional:=http;provide:=test.activator.Activator; multiple:=http",
"test.activator.Activator");
assertEquals("test.activator.Activator", xpath.evaluate("/component/implementation/@class", doc));
assertEquals("org.osgi.service.http.HttpService",
xpath.evaluate("/component/reference[@name='http']/@interface", doc));
// there are no bind/unbind methods...
assertEquals("", xpath.evaluate("/component/reference[@name='http']/@bind", doc));
assertEquals("", xpath.evaluate("/component/reference[@name='http']/@unbind", doc));
assertEquals("0..n", xpath.evaluate("/component/reference[@name='http']/@cardinality", doc));
assertEquals("dynamic", xpath.evaluate("/component/reference[@name='http']/@policy", doc));
assertEquals("test.activator.Activator", xpath.evaluate("/component/service/provide/@interface", doc));
}
/**
* Check if a bad filter on a service component causes an error.
*
* @throws Exception
*/
public void testBadFilter() throws Exception {
java.util.Properties p = new Properties();
p.put(Analyzer.EXPORT_PACKAGE, "test.activator,org.osgi.service.http");
p.put(Analyzer.IMPORT_PACKAGE, "*");
p.put(Analyzer.SERVICE_COMPONENT,
"test.activator.Activator;http=\"org.osgi.service.http.HttpService(|p=1)(p=2))\"");
Builder b = new Builder();
b.setClasspath(new File[] {
IO.getFile("bin"), IO.getFile("jar/osgi.jar")
});
b.setProperties(p);
b.build();
System.err.println(b.getErrors());
System.err.println(b.getWarnings());
assertEquals(1, b.getErrors().size());
assertTrue(b.getErrors().get(0).indexOf("Invalid target filter") >= 0);
assertEquals(0, b.getWarnings().size());
}
/**
* Check if we can set a target filter
*
* @throws Exception
*/
public void testFilter() throws Exception {
Element component = setup("test.activator.Activator;http=\"org.osgi.service.http.HttpService(|(p=1)(p=2))\"");
Element implementation = (Element) component.getElementsByTagName("implementation").item(0);
assertEquals(null, implementation.getNamespaceURI());
assertEquals("test.activator.Activator", implementation.getAttribute("class"));
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("org.osgi.service.http.HttpService", reference.getAttribute("interface"));
// we actually check for the methods and don't add them blindly
assertEquals("", reference.getAttribute("bind"));
assertEquals("", reference.getAttribute("unbind"));
assertEquals("(|(p=1)(p=2))", reference.getAttribute("target"));
}
/**
* Standard activator with reference to http.
*
* @throws Exception
*/
public void testSimple() throws Exception {
Element component = setup("test.activator.Activator;http=org.osgi.service.http.HttpService?");
Element implementation = (Element) component.getElementsByTagName("implementation").item(0);
assertEquals(null, implementation.getNamespaceURI());
assertEquals("test.activator.Activator", implementation.getAttribute("class"));
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("org.osgi.service.http.HttpService", reference.getAttribute("interface"));
assertEquals("", reference.getAttribute("bind"));
assertEquals("", reference.getAttribute("unbind"));
assertEquals("", reference.getAttribute("target"));
assertEquals("0..1", reference.getAttribute("cardinality"));
assertEquals("dynamic", reference.getAttribute("policy"));
}
/**
* Standard activator with reference to http.
*
* @throws Exception
*/
public void testQuestion() throws Exception {
Element component = setup("test.activator.Activator;http=org.osgi.service.http.HttpService?");
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("0..1", reference.getAttribute("cardinality"));
assertEquals("dynamic", reference.getAttribute("policy"));
}
public void testStar() throws Exception {
Element component = setup("test.activator.Activator;http=org.osgi.service.http.HttpService*");
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("0..n", reference.getAttribute("cardinality"));
assertEquals("dynamic", reference.getAttribute("policy"));
}
public void testPlus() throws Exception {
Element component = setup("test.activator.Activator;http=org.osgi.service.http.HttpService+");
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("1..n", reference.getAttribute("cardinality"));
assertEquals("dynamic", reference.getAttribute("policy"));
}
public void testTilde() throws Exception {
Element component = setup("test.activator.Activator;http=org.osgi.service.http.HttpService~");
Element reference = (Element) component.getElementsByTagName("reference").item(0);
assertEquals("0..1", reference.getAttribute("cardinality"));
assertEquals("", reference.getAttribute("policy"));
}
}