blob: f67628dfded8b2b3bcca04360b98ae3c7b4d46d9 [file] [log] [blame]
/*
* 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.
*/
package org.apache.felix.configurator.impl;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Vector;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
public class ConfiguratorTest {
private Configurator configurator;
private BundleContext bundleContext;
private Bundle bundle;
private ConfigurationAdmin configurationAdmin;
private ServiceReference<ConfigurationAdmin> caRef;
@SuppressWarnings("unchecked")
@Before public void setup() throws IOException {
bundle = mock(Bundle.class);
when(bundle.getBundleId()).thenReturn(42L);
when(bundle.getState()).thenReturn(Bundle.ACTIVE);
bundleContext = mock(BundleContext.class);
when(bundle.getBundleContext()).thenReturn(bundleContext);
when(bundleContext.getBundle()).thenReturn(bundle);
when(bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION)).thenReturn(bundle);
when(bundleContext.getBundle(42)).thenReturn(bundle);
when(bundleContext.getBundles()).thenReturn(new Bundle[0]);
when(bundleContext.getDataFile("binaries" + File.separatorChar + ".check")).thenReturn(Files.createTempDirectory("test").toFile());
caRef = mock(ServiceReference.class);
when(caRef.getBundle()).thenReturn(bundle);
configurationAdmin = mock(ConfigurationAdmin.class);
when(bundleContext.getService(caRef)).thenReturn(configurationAdmin);
configurator = new Configurator(bundleContext, Collections.singletonList(caRef));
}
private Bundle setupBundle(final long id) throws Exception {
return setupBundle(id, String.valueOf(id));
}
private long lastModified = 1;
private Bundle setupBundle(final long id, final String jsonName) throws Exception {
final Bundle b = mock(Bundle.class);
when(b.getBundleId()).thenReturn(id);
when(b.getLastModified()).thenReturn(lastModified);
lastModified++;
when(b.getState()).thenReturn(Bundle.ACTIVE);
final BundleWiring wiring = mock(BundleWiring.class);
when(b.adapt(BundleWiring.class)).thenReturn(wiring);
final BundleRequirement req = mock(BundleRequirement.class);
when(wiring.getRequirements(Util.NS_OSGI_EXTENDER)).thenReturn(Collections.singletonList(req));
final BundleWire wire = mock(BundleWire.class);
when(wire.getProviderWiring()).thenReturn(wiring);
when(wire.getRequirement()).thenReturn(req);
when(wiring.getBundle()).thenReturn(bundle);
when(wiring.getRequiredWires(Util.NS_OSGI_EXTENDER)).thenReturn(Collections.singletonList(wire));
final Vector<URL> urls = new Vector<>();
urls.add(this.getClass().getResource("/bundles/" + jsonName + ".json"));
when(b.findEntries("OSGI-INF/configurator", "*.json", false)).thenReturn(urls.elements());
final BundleContext bContext = mock(BundleContext.class);
when(b.getBundleContext()).thenReturn(bContext);
when(bContext.getServiceReferences(ConfigurationAdmin.class, null)).thenReturn(Collections.singleton(caRef));
when(bundleContext.getBundle(id)).thenReturn(b);
return b;
}
@Test public void testSimpleAddRemove() throws Exception {
final Bundle b = setupBundle(1);
Configuration c1 = mock(Configuration.class);
Configuration c2 = mock(Configuration.class);
when(configurationAdmin.getConfiguration("a", "?")).thenReturn(c1);
when(configurationAdmin.getConfiguration("b", "?")).thenReturn(c2);
when(c1.getChangeCount()).thenReturn(1L);
when(c2.getChangeCount()).thenReturn(1L);
configurator.processAddBundle(b);
configurator.process();
when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=a)")).thenReturn(new Configuration[] {c1});
when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=b)")).thenReturn(new Configuration[] {c2});
final Dictionary<String, Object> props1 = new Hashtable<>();
props1.put("foo", "bar");
verify(c1).updateIfDifferent(props1);
final Dictionary<String, Object> props2 = new Hashtable<>();
props2.put("x", "y");
verify(c2).updateIfDifferent(props2);
configurator.processRemoveBundle(1);
configurator.process();
verify(c1).delete();
verify(c2).delete();
}
@Test public void testSimpleAddUpdate() throws Exception {
final Bundle bV1 = setupBundle(2);
final Bundle bV2 = setupBundle(2, "2a");
// initial - no configuration
Configuration c1 = mock(Configuration.class);
Configuration c2 = mock(Configuration.class);
Configuration c3 = mock(Configuration.class);
when(c1.getChangeCount()).thenReturn(1L);
when(c2.getChangeCount()).thenReturn(1L);
when(c3.getChangeCount()).thenReturn(1L);
when(configurationAdmin.getConfiguration("a", "?")).thenReturn(c1);
when(configurationAdmin.getConfiguration("b", "?")).thenReturn(c2);
when(configurationAdmin.getConfiguration("c", "?")).thenReturn(c3);
configurator.processAddBundle(bV1);
configurator.process();
final Dictionary<String, Object> props1a = new Hashtable<>();
props1a.put("foo", "bar2");
final Dictionary<String, Object> props1b = new Hashtable<>();
props1b.put("x", "y2");
final Dictionary<String, Object> props1c = new Hashtable<>();
props1c.put("c", "1");
InOrder inorder = inOrder(c1,c2,c3);
inorder.verify(c1).updateIfDifferent(props1a);
inorder.verify(c1).getChangeCount();
inorder.verify(c2).updateIfDifferent(props1b);
inorder.verify(c2).getChangeCount();
inorder.verify(c3).updateIfDifferent(props1c);
inorder.verify(c3).getChangeCount();
inorder.verifyNoMoreInteractions();
// c1 and c2 are changed manually -> increase change count, c3 is not changed manually
when(c1.getChangeCount()).thenReturn(2L);
when(c2.getChangeCount()).thenReturn(2L);
when(configurationAdmin.listConfigurations("(service.pid=a)")).thenReturn(new Configuration[] {c1});
when(configurationAdmin.listConfigurations("(service.pid=b)")).thenReturn(new Configuration[] {c2});
when(configurationAdmin.listConfigurations("(service.pid=c)")).thenReturn(new Configuration[] {c3});
configurator.processAddBundle(bV2);
configurator.process();
final Dictionary<String, Object> props2a = new Hashtable<>();
props2a.put("foo", "bar3");
final Dictionary<String, Object> props2c = new Hashtable<>();
props2c.put("c", "2");
inorder = inOrder(c1, c2, c3);
inorder.verify(c1).updateIfDifferent(props2a);
inorder.verify(c1).getChangeCount();
inorder.verify(c2).getChangeCount();
inorder.verify(c3).updateIfDifferent(props2c);
inorder.verify(c3).getChangeCount();
inorder.verifyNoMoreInteractions();
}
@Test public void testSimpleRankingRemove() throws Exception {
final Bundle b1 = setupBundle(1);
final Bundle b2 = setupBundle(2);
Configuration c1 = mock(Configuration.class);
Configuration c2 = mock(Configuration.class);
Configuration c3 = mock(Configuration.class);
when(configurationAdmin.getConfiguration("a", "?")).thenReturn(c1);
when(configurationAdmin.getConfiguration("b", "?")).thenReturn(c2);
when(configurationAdmin.getConfiguration("c", "?")).thenReturn(c3);
when(c1.getChangeCount()).thenReturn(1L);
when(c2.getChangeCount()).thenReturn(1L);
when(c3.getChangeCount()).thenReturn(1L);
configurator.processAddBundle(b2);
configurator.process();
when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=a)")).thenReturn(new Configuration[] {c1});
when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=b)")).thenReturn(new Configuration[] {c2});
when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=c)")).thenReturn(new Configuration[] {c3});
final Dictionary<String, Object> props1 = new Hashtable<>();
props1.put("foo", "bar2");
final Dictionary<String, Object> props2 = new Hashtable<>();
props2.put("x", "y2");
configurator.processAddBundle(b1);
configurator.process();
final Dictionary<String, Object> props3 = new Hashtable<>();
props3.put("foo", "bar");
final Dictionary<String, Object> props4 = new Hashtable<>();
props4.put("x", "y");
configurator.processRemoveBundle(1);
configurator.process();
configurator.processRemoveBundle(2);
configurator.process();
InOrder inorder = inOrder(c1, c2);
inorder.verify(c1).updateIfDifferent(props1);
inorder.verify(c2).updateIfDifferent(props2);
inorder.verify(c1).updateIfDifferent(props3);
inorder.verify(c2).updateIfDifferent(props4);
inorder.verify(c1).updateIfDifferent(props1);
inorder.verify(c2).updateIfDifferent(props2);
inorder.verify(c1).delete();
inorder.verify(c2).delete();
inorder.verifyNoMoreInteractions();
}
}