/*
* Licensed 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.
*/
package org.apache.aries.subsystem.itests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.aries.subsystem.core.archive.SubsystemImportServiceHeader;
import org.apache.aries.subsystem.itests.util.GenericMetadataWrapper;
import org.apache.aries.util.manifest.ManifestHeaderProcessor;
import org.apache.aries.util.manifest.ManifestHeaderProcessor.GenericMetadata;
import org.junit.Assert;
import org.junit.Test;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.Version;
import org.osgi.service.subsystem.Subsystem;
import org.osgi.service.subsystem.SubsystemConstants;
import org.osgi.service.subsystem.SubsystemException;
public class ServiceDependencyTest extends SubsystemTest {
/*
* Subsystem-SymbolicName: application.a.esa
* Subsystem-Content: bundle.a.jar
*/
private static final String APPLICATION_A = "application.a.esa";
/*
* Subsystem-SymbolicName: application.b.esa
* Subsystem-Content: bundle.b.jar
*/
private static final String APPLICATION_B = "application.b.esa";
/*
* Subsystem-SymbolicName: application.b.esa
* Subsystem-Content: bundle.a.jar, bundle.b.jar
*/
private static final String APPLICATION_C = "application.c.esa";
/*
* Subsystem-SymbolicName: application.d.esa
* Subsystem-Content: bundle.a.jar, composite.a.esa
*/
private static final String APPLICATION_D = "application.d.esa";
/*
* Subsystem-SymbolicName: composite.a.esa
* Subsystem-Content: bundle.b.jar
*/
private static final String COMPOSITE_A = "composite.a.esa";
/*
* Bundle-SymbolicName: bundle.a.jar
* Bundle-Blueprint: OSGI-INF/blueprint/*.xml
*
* <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
* <reference interface="bundle.b" filter="(&(active=true)(mode=shared))"/>
* <service interface="bundle.a" ref="bundle.a"/>
* </blueprint>
*/
private static final String BUNDLE_A = "bundle.a.jar";
/*
* Bundle-SymbolicName: bundle.b.jar
* Bundle-Blueprint: OSGI-INF/blueprint/*.xml
*
* <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
* <reference interface="bundle.a" availability="optional"/>
* <service ref="bundle.b">
* <interfaces>
* <value>bundle.b</value>
* <value>bundle.b1</value>
* </interfaces>
* <service-properties>
* <entry key="active">
* <value type="java.lang.Boolean">true</value>
* </entry>
* <entry key="mode" value="shared"/>
* </service-properties>
* </service>
* </blueprint>
*/
private static final String BUNDLE_B = "bundle.b.jar";
private static void createApplicationA() throws IOException {
createApplicationAManifest();
createSubsystem(APPLICATION_A, BUNDLE_A);
}
private static void createApplicationB() throws IOException {
createApplicationBManifest();
createSubsystem(APPLICATION_B, BUNDLE_B);
}
private static void createApplicationC() throws IOException {
createApplicationCManifest();
createSubsystem(APPLICATION_C, BUNDLE_A, BUNDLE_B);
}
private static void createApplicationD() throws IOException {
createApplicationDManifest();
createSubsystem(APPLICATION_D, BUNDLE_A, COMPOSITE_A);
}
private static void createApplicationAManifest() throws IOException {
createBasicApplicationManifest(APPLICATION_A);
}
private static void createApplicationBManifest() throws IOException {
createBasicApplicationManifest(APPLICATION_B);
}
private static void createApplicationCManifest() throws IOException {
createBasicApplicationManifest(APPLICATION_C);
}
private static void createApplicationDManifest() throws IOException {
createBasicApplicationManifest(APPLICATION_D);
}
private static void createBasicApplicationManifest(String symbolicName) throws IOException {
createBasicSubsystemManifest(symbolicName, null, null);
}
private static void createBasicSubsystemManifest(String symbolicName, Version version, String type) throws IOException {
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, symbolicName);
if (version != null)
attributes.put(SubsystemConstants.SUBSYSTEM_VERSION, version.toString());
if (type != null)
attributes.put(SubsystemConstants.SUBSYSTEM_TYPE, type);
createManifest(symbolicName + ".mf", attributes);
}
private static void createBundleA() throws IOException {
createBlueprintBundle(
BUNDLE_A,
new StringBuilder()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.append("<blueprint ")
.append("xmlns=\"http://www.osgi.org/xmlns/blueprint/v1.0.0\">")
.append("<reference ")
.append("interface=\"bundle.b\" ")
.append("filter=\"(active=true)(mode=shared)\"")
.append("/>")
.append("<service ")
.append("interface=\"bundle.a\" ")
.append("ref=\"bundle.a\"")
.append("/>")
.append("</blueprint>")
.toString());
}
private static void createBundleB() throws IOException {
createBlueprintBundle(
BUNDLE_B,
new StringBuilder()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.append("<blueprint ")
.append("xmlns=\"http://www.osgi.org/xmlns/blueprint/v1.0.0\">")
.append("<reference ")
.append("interface=\"bundle.a\" ")
.append("availability=\"optional\"")
.append("/>")
.append("<service ref=\"bundle.b\">")
.append("<interfaces>")
.append("<value>bundle.b</value>")
.append("<value>bundle.b1</value>")
.append("</interfaces>")
.append("<service-properties>")
.append("<entry key=\"active\">")
.append("<value type=\"java.lang.Boolean\">true</value>")
.append("</entry>")
.append("<entry key=\"mode\" value=\"shared\"/>")
.append("</service-properties>")
.append("</service>")
.append("</blueprint>")
.toString());
}
private static void createCompositeA() throws IOException {
createCompositeAManifest();
createSubsystem(COMPOSITE_A, BUNDLE_B);
}
private static void createCompositeAManifest() throws IOException {
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, COMPOSITE_A);
attributes.put(SubsystemConstants.SUBSYSTEM_TYPE, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
attributes.put(
SubsystemConstants.SUBSYSTEM_EXPORTSERVICE,
"bundle.b;filter:=\"(&(active=true)(mode=shared))\"");
createManifest(COMPOSITE_A + ".mf", attributes);
}
@Override
public void createApplications() throws Exception {
createBundleA();
createBundleB();
createApplicationA();
createApplicationB();
createApplicationC();
createCompositeA();
createApplicationD();
}
//@Test
public void testImportServiceDependencySatisfiedByChild() throws Exception {
try {
Subsystem subsystem = installSubsystemFromFile(APPLICATION_D);
try {
assertNull(
"Generated application Subsystem-ImportService header when dependency satisfied by child",
subsystem.getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_IMPORTSERVICE));
assertSubsystemExportServiceHeader(
subsystem.getChildren().iterator().next(),
"bundle.b;filter:=\"(&(active=true)(mode=shared))\"");
}
finally {
uninstallSubsystemSilently(subsystem);
}
}
catch (SubsystemException e) {
e.printStackTrace();
fail("Installation must succeed if missing service dependency is satisfied");
}
}
@Test
public void testImportServiceDependencySatisfiedByContent() throws Exception {
try {
Subsystem subsystem = installSubsystemFromFile(APPLICATION_C);
try {
assertNull(
"Generated application Subsystem-ImportService header when dependency satisfied by content",
subsystem.getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_IMPORTSERVICE));
}
finally {
uninstallSubsystemSilently(subsystem);
}
}
catch (SubsystemException e) {
e.printStackTrace();
fail("Installation must succeed if service dependency is satisfied");
}
}
@Test
public void testImportServiceDependencySatisfiedByParent() throws Exception {
try {
Subsystem parent = installSubsystemFromFile(APPLICATION_B);
try {
Subsystem child = installSubsystemFromFile(parent, APPLICATION_A);
try {
assertSubsystemImportServiceHeader(child, "bundle.b;filter:=\"(&(active=true)(mode=shared))\";resolution:=mandatory;cardinality:=single;effective:=active");
}
finally {
uninstallSubsystemSilently(child);
}
}
catch (SubsystemException e) {
e.printStackTrace();
fail("Installation must succeed if service dependency is satisfied");
}
finally {
uninstallSubsystemSilently(parent);
}
}
catch (SubsystemException e) {
e.printStackTrace();
fail("Installation must succeed if missing service dependency is optional");
}
}
@Test
public void testMissingImportServiceDependencyMandatory() throws Exception {
try {
Subsystem subsystem = installSubsystemFromFile(APPLICATION_A);
uninstallSubsystemSilently(subsystem);
fail("Installation must fail due to missing service dependency");
}
catch (SubsystemException e) {
// Okay.
}
}
@Test
public void testMissingImportServiceDependencyOptional() throws Exception {
try {
Subsystem subsystem = installSubsystemFromFile(APPLICATION_B);
try {
assertSubsystemImportServiceHeader(subsystem, "bundle.a;resolution:=optional;cardinality:=single;effective:=active");
}
finally {
uninstallSubsystemSilently(subsystem);
}
}
catch (SubsystemException e) {
e.printStackTrace();
fail("Installation must succeed if missing service dependency is optional");
}
}
private void assertSubsystemExportServiceHeader(Subsystem subsystem, String value) throws InvalidSyntaxException {
String header = assertHeaderExists(subsystem, SubsystemConstants.SUBSYSTEM_EXPORTSERVICE);
List<GenericMetadata> actual = ManifestHeaderProcessor.parseRequirementString(header);
List<GenericMetadata> expected = ManifestHeaderProcessor.parseRequirementString(value);
Assert.assertEquals("Wrong number of clauses", expected.size(), actual.size());
for (int i = 0; i < expected.size(); i++)
assertEquals("Wrong clause", new GenericMetadataWrapper(expected.get(i)), new GenericMetadataWrapper(actual.get(i)));
}
private void assertSubsystemImportServiceHeader(Subsystem subsystem, String value) throws InvalidSyntaxException {
String header = assertHeaderExists(subsystem, SubsystemConstants.SUBSYSTEM_IMPORTSERVICE);
SubsystemImportServiceHeader actual = new SubsystemImportServiceHeader(header);
SubsystemImportServiceHeader expected = new SubsystemImportServiceHeader(value);
Collection<SubsystemImportServiceHeader.Clause> actualClauses = actual.getClauses();
Collection<SubsystemImportServiceHeader.Clause> expectedClauses = expected.getClauses();
Assert.assertEquals("Wrong number of clauses", expectedClauses.size(), actualClauses.size());
Iterator<SubsystemImportServiceHeader.Clause> actualItr = actualClauses.iterator();
Iterator<SubsystemImportServiceHeader.Clause> expectedItr = expectedClauses.iterator();
while (expectedItr.hasNext()) {
assertEquals("Wrong clause", expectedItr.next(), actualItr.next());
}
}
}