this.clientDispatchers = new ArrayList<>();
}
- @Override
@After
public void tearDown() throws Exception {
this.serverDispatcher.close();
this.bgpActivator.close();
this.clientDispatchers.forEach(BGPDispatcherImpl::close);
this.clientDispatchers = null;
- super.tearDown();
}
void sendRouteAndCheckIsOnLocRib(final BGPSessionImpl session, final Ipv4Prefix prefix, final long localPreference,
return this.domTransWrite;
}
- @Override
@After
public void tearDown() {
this.a1.close();
this.codecsRegistry = new ConstantCodecsRegistry(serializer);
}
- @Override
@After
public void tearDown() {
this.lsact.close();
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.bgpcep.config.loader.bmp;
import static org.junit.Assert.assertEquals;
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>mdsal-binding-dom-codec-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-dom-codec-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-runtime-api</artifactId>
+ </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.cmpn</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
<!-- test scope dependencies -->
<dependency>
*/
package org.opendaylight.bgpcep.config.loader.impl;
-import static java.util.Objects.requireNonNull;
-
import com.google.common.base.Stopwatch;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
-import java.nio.file.ClosedWatchServiceException;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.bgpcep.config.loader.spi.ConfigFileProcessor;
import org.opendaylight.bgpcep.config.loader.spi.ConfigLoader;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.AbstractRegistration;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
-public final class AbstractConfigLoader implements ConfigLoader, AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigLoader.class);
- private static final String INTERRUPTED = "InterruptedException";
- private static final String EXTENSION = "-.*\\.xml";
- private static final String INITIAL = "^";
- private static final String READ = "rw";
- private static final long TIMEOUT_SECONDS = 5;
- @GuardedBy("this")
- private final Map<String, ConfigFileProcessor> configServices = new HashMap<>();
- private final EffectiveModelContext schemaContext;
- private final BindingNormalizedNodeSerializer bindingSerializer;
- private final String path;
- private final Thread watcherThread;
- private final File file;
- @GuardedBy("this")
- private boolean closed = false;
-
- public AbstractConfigLoader(final EffectiveModelContext schemaContext,
- final BindingNormalizedNodeSerializer bindingSerializer, final FileWatcher fileWatcher) {
- this.schemaContext = requireNonNull(schemaContext);
- this.bindingSerializer = requireNonNull(bindingSerializer);
- this.path = requireNonNull(fileWatcher.getPathFile());
- this.file = new File(this.path);
- this.watcherThread = new Thread(new ConfigLoaderImplRunnable(requireNonNull(fileWatcher.getWatchService())));
- }
-
- public void init() {
- this.watcherThread.start();
- LOG.info("Config Loader service initiated");
- }
-
- private synchronized void handleConfigFile(final ConfigFileProcessor config, final String filename) {
- final NormalizedNode<?, ?> dto;
- try {
- dto = parseDefaultConfigFile(config, filename);
- } catch (final IOException | XMLStreamException e) {
- LOG.warn("Failed to parse config file {}", filename, e);
- return;
+/**
+ * Reference implementation of configuration loading bits, without worrying where files are actually coming from.
+ */
+abstract class AbstractConfigLoader implements ConfigLoader {
+ private final class PatternRegistration extends AbstractObjectRegistration<Pattern> {
+ PatternRegistration(final Pattern pattern) {
+ super(pattern);
}
- LOG.info("Loading initial config {}", filename);
- config.loadConfiguration(dto);
- }
-
- private NormalizedNode<?, ?> parseDefaultConfigFile(final ConfigFileProcessor config, final String filename)
- throws IOException, XMLStreamException {
- final NormalizedNodeResult result = new NormalizedNodeResult();
- final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-
- final File newFile = new File(this.path, filename);
- FileChannel channel = new RandomAccessFile(newFile, READ).getChannel();
- FileLock lock = null;
- final Stopwatch stopwatch = Stopwatch.createStarted();
- while (lock == null || stopwatch.elapsed(TimeUnit.SECONDS) > TIMEOUT_SECONDS) {
- try {
- lock = channel.tryLock();
- } catch (final IllegalStateException e) {
- //Ignore
- }
- if (lock == null) {
- try {
- Thread.sleep(100L);
- } catch (InterruptedException e) {
- LOG.warn("Failed to lock xml", e);
- }
- }
+ @Override
+ protected void removeRegistration() {
+ unregister(this);
}
+ }
- try (InputStream resourceAsStream = new FileInputStream(newFile)) {
- final XMLInputFactory factory = XMLInputFactory.newInstance();
- final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
-
- final SchemaNode schemaNode = SchemaContextUtil
- .findDataSchemaNode(this.schemaContext, config.getSchemaPath());
- try (XmlParserStream xmlParser = XmlParserStream.create(streamWriter, this.schemaContext, schemaNode)) {
- xmlParser.parse(reader);
- } catch (final URISyntaxException | XMLStreamException | IOException | SAXException e) {
- LOG.warn("Failed to parse xml", e);
- } finally {
- reader.close();
- channel.close();
- }
- }
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigLoader.class);
+ private static final String EXTENSION = "-.*\\.xml";
+ private static final String INITIAL = "^";
+ private static final String READ = "rw";
+ private static final long TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(5);
- return result.getResult();
- }
+ @GuardedBy("this")
+ private final Map<PatternRegistration, ConfigFileProcessor> configServices = new HashMap<>();
@Override
- public synchronized AbstractRegistration registerConfigFile(final ConfigFileProcessor config) {
- final String pattern = INITIAL + config.getSchemaPath().getLastComponent().getLocalName() + EXTENSION;
- this.configServices.put(pattern, config);
+ public final synchronized AbstractRegistration registerConfigFile(final ConfigFileProcessor config) {
+ final String patternStr = INITIAL + config.getSchemaPath().getLastComponent().getLocalName() + EXTENSION;
+ final Pattern pattern = Pattern.compile(patternStr);
+ final PatternRegistration reg = new PatternRegistration(pattern);
+
+ this.configServices.put(reg, config);
- final File[] fList = this.file.listFiles();
+ final File[] fList = directory().listFiles();
final List<String> newFiles = new ArrayList<>();
if (fList != null) {
for (final File newFile : fList) {
if (newFile.isFile()) {
final String filename = newFile.getName();
- if (Pattern.matches(pattern, filename)) {
+ if (pattern.matcher(filename).matches()) {
newFiles.add(filename);
}
}
for (final String filename : newFiles) {
handleConfigFile(config, filename);
}
- return new AbstractRegistration() {
- @Override
- protected void removeRegistration() {
- synchronized (AbstractConfigLoader.this) {
- AbstractConfigLoader.this.configServices.remove(pattern);
- }
- }
- };
+ return reg;
}
- @Override
- public BindingNormalizedNodeSerializer getBindingNormalizedNodeSerializer() {
- return this.bindingSerializer;
+ final synchronized void handleEvent(final String filename) {
+ configServices.entrySet().stream()
+ .filter(entry -> entry.getKey().getInstance().matcher(filename).matches())
+ .forEach(entry -> handleConfigFile(entry.getValue(), filename));
}
+ abstract @NonNull File directory();
- @Override
- public synchronized void close() {
- LOG.info("Config Loader service closed");
- this.closed = true;
- this.watcherThread.interrupt();
- }
+ abstract @NonNull EffectiveModelContext modelContext();
- private class ConfigLoaderImplRunnable implements Runnable {
- @GuardedBy("this")
- private final WatchService watchService;
+ @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+ justification = "https://github.com/spotbugs/spotbugs/issues/811")
+ private synchronized void unregister(final PatternRegistration reg) {
+ configServices.remove(reg);
+ }
- ConfigLoaderImplRunnable(final WatchService watchService) {
- this.watchService = watchService;
+ private synchronized void handleConfigFile(final ConfigFileProcessor config, final String filename) {
+ final NormalizedNode<?, ?> dto;
+ try {
+ dto = parseDefaultConfigFile(config, filename);
+ } catch (final IOException | XMLStreamException e) {
+ LOG.warn("Failed to parse config file {}", filename, e);
+ return;
}
+ LOG.info("Loading initial config {}", filename);
+ config.loadConfiguration(dto);
+ }
- @Override
- public void run() {
- while (!Thread.currentThread().isInterrupted()) {
- handleChanges();
- }
- }
+ private NormalizedNode<?, ?> parseDefaultConfigFile(final ConfigFileProcessor config, final String filename)
+ throws IOException, XMLStreamException {
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final File newFile = new File(directory(), filename);
+ try (RandomAccessFile raf = new RandomAccessFile(newFile, READ)) {
+ final FileChannel channel = raf.getChannel();
- private synchronized void handleChanges() {
- final WatchKey key;
- try {
- key = this.watchService.take();
- } catch (final InterruptedException | ClosedWatchServiceException e) {
- if (!AbstractConfigLoader.this.closed) {
- LOG.warn(INTERRUPTED, e);
- Thread.currentThread().interrupt();
+ FileLock lock = null;
+ final Stopwatch stopwatch = Stopwatch.createStarted();
+ while (lock == null || stopwatch.elapsed(TimeUnit.NANOSECONDS) > TIMEOUT_NANOS) {
+ try {
+ lock = channel.tryLock();
+ } catch (final IllegalStateException e) {
+ //Ignore
+ }
+ if (lock == null) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ LOG.warn("Failed to lock xml", e);
+ }
}
- return;
}
- if (key != null) {
- final List<String> fileNames = key.pollEvents()
- .stream().map(event -> event.context().toString())
- .collect(Collectors.toList());
- fileNames.forEach(this::handleEvent);
-
- final boolean reset = key.reset();
- if (!reset) {
- LOG.warn("Could not reset the watch key.");
+ try (InputStream resourceAsStream = new FileInputStream(newFile)) {
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final EffectiveModelContext modelContext = modelContext();
+ final SchemaNode schemaNode = SchemaContextUtil.findDataSchemaNode(modelContext(),
+ config.getSchemaPath());
+ try (XmlParserStream xmlParser = XmlParserStream.create(streamWriter, modelContext, schemaNode)) {
+ xmlParser.parse(reader);
+ } catch (final URISyntaxException | XMLStreamException | IOException | SAXException e) {
+ LOG.warn("Failed to parse xml", e);
+ } finally {
+ reader.close();
}
}
}
- private synchronized void handleEvent(final String filename) {
- AbstractConfigLoader.this.configServices.entrySet().stream()
- .filter(entry -> Pattern.matches(entry.getKey(), filename))
- .forEach(entry -> handleConfigFile(entry.getValue(), filename));
- }
+ return result.getResult();
}
}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.config.loader.impl;
+
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An {@link AbstractConfigLoader} which additionally talks to a {@link WatchService}.
+ */
+abstract class AbstractWatchingConfigLoader extends AbstractConfigLoader {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractWatchingConfigLoader.class);
+
+ private final AtomicBoolean closed = new AtomicBoolean();
+ private final Thread watcherThread;
+
+ AbstractWatchingConfigLoader() {
+ watcherThread = new Thread(this::dispatchEvents, "Config Loader Watcher Thread");
+ watcherThread.setDaemon(true);
+ }
+
+ final void start() {
+ watcherThread.start();
+ LOG.info("Config Loader service started");
+ }
+
+ final void stop() {
+ LOG.info("Config Loader service stopping");
+
+ closed.set(true);
+ watcherThread.interrupt();
+
+ try {
+ this.watcherThread.join();
+ } catch (InterruptedException e) {
+ LOG.warn("Interrupted while waiting for watcher thread to terminate", e);
+ }
+ LOG.info("Config Loader service stopped");
+ }
+
+ abstract WatchKey takeEvent() throws InterruptedException;
+
+ private void dispatchEvents() {
+ while (!closed.get()) {
+ final WatchKey key;
+ try {
+ key = takeEvent();
+ } catch (final InterruptedException | ClosedWatchServiceException e) {
+ if (!closed.get()) {
+ LOG.warn("Exception while waiting for events, exiting", e);
+ Thread.currentThread().interrupt();
+ }
+ return;
+ }
+
+ if (key != null) {
+ key.pollEvents().stream().map(event -> event.context().toString()).forEach(this::handleEvent);
+ if (!key.reset()) {
+ LOG.warn("Could not reset the watch key.");
+ }
+ }
+ }
+ }
+}
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchService;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Singleton;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class DefaultFileWatcher implements FileWatcher, AutoCloseable {
+@Singleton
+@Component(immediate = true, service = FileWatcher.class)
+public final class DefaultFileWatcher extends AbstractRegistration implements FileWatcher {
private static final Logger LOG = LoggerFactory.getLogger(DefaultFileWatcher.class);
- private static final String INTERRUPTED = "InterruptedException";
//BGPCEP config folder OS agnostic path
private static final Path PATH = Paths.get("etc","opendaylight","bgpcep");
+
private final WatchService watchService;
public DefaultFileWatcher() throws IOException {
- this.watchService = FileSystems.getDefault().newWatchService();
+ watchService = FileSystems.getDefault().newWatchService();
+ Runtime.getRuntime().addShutdownHook(new Thread(this::close));
+ }
+
+ @Override
+ public String getPathFile() {
+ return PATH.toString();
+ }
+
+ @Override
+ public WatchService getWatchService() {
+ return watchService;
+ }
+
+ @Activate
+ @PostConstruct
+ public void activate() throws IOException {
final File file = new File(PATH.toString());
if (!file.exists()) {
if (!file.mkdirs()) {
}
}
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- try {
- DefaultFileWatcher.this.watchService.close();
- } catch (final IOException e) {
- LOG.warn(INTERRUPTED, e);
- }
- }));
PATH.register(this.watchService, OVERFLOW, ENTRY_CREATE);
- LOG.info("File Watcher service initiated");
+ LOG.info("File Watcher service started");
}
- @Override
- public String getPathFile() {
- return PATH.toString();
- }
-
- @Override
- public synchronized WatchService getWatchService() {
- return this.watchService;
+ @Deactivate
+ @PreDestroy
+ public void deactivate() {
+ // Just route to close(), it will do the right thing
+ close();
}
@Override
- public synchronized void close() throws Exception {
- if (this.watchService != null) {
- this.watchService.close();
+ protected void removeRegistration() {
+ try {
+ watchService.close();
+ } catch (IOException e) {
+ LOG.warn("Failed to close watch service", e);
}
+ LOG.info("File Watcher service stopped");
}
}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.config.loader.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.io.File;
+import java.nio.file.WatchKey;
+import org.opendaylight.bgpcep.config.loader.spi.ConfigLoader;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+
+@Beta
+@Component(immediate = true, service = ConfigLoader.class)
+public final class OSGiConfigLoader extends AbstractWatchingConfigLoader {
+ @Reference
+ FileWatcher watcher;
+
+ @Reference
+ volatile BindingDOMCodecServices codecServices;
+
+ private File directory;
+
+ @Override
+ public BindingNormalizedNodeSerializer getBindingNormalizedNodeSerializer() {
+ return verifyNotNull(codecServices);
+ }
+
+ @Override
+ EffectiveModelContext modelContext() {
+ return codecServices.getRuntimeContext().getEffectiveModelContext();
+ }
+
+ @Activate
+ void activate() {
+ directory = new File(watcher.getPathFile());
+ start();
+ }
+
+ @Deactivate
+ void deactivate() {
+ try {
+ stop();
+ } finally {
+ directory = null;
+ }
+ }
+
+ @Override
+ File directory() {
+ return verifyNotNull(directory);
+ }
+
+ @Override
+ WatchKey takeEvent() throws InterruptedException {
+ return watcher.getWatchService().take();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.config.loader.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.File;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+
+@Beta
+@Singleton
+public final class SimpleConfigLoader extends AbstractWatchingConfigLoader implements AutoCloseable {
+ private final @NonNull BindingNormalizedNodeSerializer serializer;
+ private final @NonNull EffectiveModelContext modelContext;
+ private final @NonNull WatchService watchService;
+ private final @NonNull File directory;
+
+ @Inject
+ public SimpleConfigLoader(final FileWatcher fileWatcher, final BindingDOMCodecServices codec) {
+ this.serializer = requireNonNull(codec);
+ this.modelContext = codec.getRuntimeContext().getEffectiveModelContext();
+ this.watchService = fileWatcher.getWatchService();
+ this.directory = new File(fileWatcher.getPathFile());
+ }
+
+ @Override
+ public BindingNormalizedNodeSerializer getBindingNormalizedNodeSerializer() {
+ return serializer;
+ }
+
+ @PostConstruct
+ public void init() {
+ start();
+ }
+
+ @Override
+ @PreDestroy
+ public void close() {
+ stop();
+ }
+
+ @Override
+ EffectiveModelContext modelContext() {
+ return modelContext;
+ }
+
+ @Override
+ File directory() {
+ return directory;
+ }
+
+ @Override
+ WatchKey takeEvent() throws InterruptedException {
+ return watchService.take();
+ }
+}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (c) 2017. AT&T Intellectual Property. All rights reserved.
- ~
- ~ This program and the accompanying materials are made available under the
- ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
- ~ and is available at http://www.eclipse.org/legal/epl-v10.html
- -->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
- xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
- <odl:static-reference id="domSchemaService" interface="org.opendaylight.mdsal.dom.api.DOMSchemaService"/>
- <odl:static-reference id="mappingCodec"
- interface="org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer"/>
-
- <bean id="filewatcher" class="org.opendaylight.bgpcep.config.loader.impl.DefaultFileWatcher"
- destroy-method="close"/>
-
- <bean id="configLoader" class="org.opendaylight.bgpcep.config.loader.impl.AbstractConfigLoader"
- init-method="init"
- destroy-method="close">
- <argument>
- <bean factory-ref="domSchemaService" factory-method="getGlobalContext"/>
- </argument>
- <argument ref="mappingCodec"/>
- <argument ref="filewatcher"/>
- </bean>
-
- <service ref="configLoader" interface="org.opendaylight.bgpcep.config.loader.spi.ConfigLoader"/>
-</blueprint>
package org.opendaylight.bgpcep.config.loader.impl;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doNothing;
-import java.nio.file.WatchEvent;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.junit.After;
+import java.io.File;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.opendaylight.mdsal.binding.dom.adapter.AdapterContext;
import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTestCustomizer;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
public abstract class AbstractConfigLoaderTest extends AbstractConcurrentDataBrokerTest {
- @GuardedBy("this")
- private final List<WatchEvent<?>> eventList = new CopyOnWriteArrayList<>();
- protected AbstractConfigLoader configLoader;
- @Mock
- private WatchService watchService;
+ protected final class TestConfigLoader extends AbstractConfigLoader {
+ @Override
+ public BindingNormalizedNodeSerializer getBindingNormalizedNodeSerializer() {
+ return mappingService.currentSerializer();
+ }
+
+ @Override
+ File directory() {
+ return new File(getResourceFolder());
+ }
+
+ @Override
+ @SuppressWarnings("checkstyle:illegalCatch")
+ EffectiveModelContext modelContext() {
+ try {
+ return getSchemaContext();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to acquire schema context", e);
+ }
+ }
+
+ public void triggerEvent(final String filename) {
+ handleEvent(filename);
+ }
+ }
+
+ protected final TestConfigLoader configLoader = new TestConfigLoader();
@Mock
ConfigFileProcessor processor;
- @Mock
- private WatchKey watchKey;
- @Mock
- private WatchEvent<?> watchEvent;
- @Mock
- private FileWatcher fileWatcher;
protected AdapterContext mappingService;
protected DOMSchemaService schemaService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doAnswer(invocation -> true).when(this.watchKey).reset();
- doReturn(this.eventList).when(this.watchKey).pollEvents();
- doReturn(this.watchKey).when(this.watchService).take();
- doReturn("watchKey").when(this.watchKey).toString();
- doReturn("watchService").when(this.watchService).toString();
- doReturn("watchEvent").when(this.watchEvent).toString();
- doReturn(getResourceFolder()).when(this.fileWatcher).getPathFile();
- doReturn(this.watchService).when(this.fileWatcher).getWatchService();
- doAnswer(invocation -> {
- clearEvent();
- return null;
- }).when(this.processor).loadConfiguration(any());
- this.configLoader = new AbstractConfigLoader(getSchemaContext(), this.mappingService.currentSerializer(),
- this.fileWatcher);
- this.configLoader.init();
+ doNothing().when(processor).loadConfiguration(any());
}
@Override
- protected final AbstractDataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+ protected AbstractDataBrokerTestCustomizer createDataBrokerTestCustomizer() {
final AbstractDataBrokerTestCustomizer customizer = super.createDataBrokerTestCustomizer();
this.mappingService = customizer.getAdapterContext();
this.schemaService = customizer.getSchemaService();
return customizer;
}
- private synchronized void clearEvent() {
- this.eventList.clear();
- }
-
protected String getResourceFolder() {
return ClassLoader.getSystemClassLoader().getResource("initial").getPath();
}
-
- protected synchronized void triggerEvent(final String filename) {
- doReturn(filename).when(this.watchEvent).context();
- this.eventList.add(this.watchEvent);
- }
-
- @After
- public void tearDown() throws Exception {
- this.configLoader.close();
- }
}
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.Before;
final AbstractRegistration ticket = this.configLoader.registerConfigFile(this.processor);
verify(this.processor).loadConfiguration(any());
- triggerEvent("protocols-config.xml");
- verify(this.processor, timeout(20000).times(2)).loadConfiguration(any());
+ configLoader.triggerEvent("protocols-config.xml");
+ verify(this.processor, times(2)).loadConfiguration(any());
ticket.close();
- triggerEvent("protocols-config.xml");
- verify(this.processor, timeout(20000).times(2)).loadConfiguration(any());
+ configLoader.triggerEvent("protocols-config.xml");
+ verify(this.processor, times(2)).loadConfiguration(any());
}
}
@Test
public void bgpFileWatcherTest() throws Exception {
- final DefaultFileWatcher bgpFileWatcher = new DefaultFileWatcher();
- assertEquals(PATH, bgpFileWatcher.getPathFile());
- assertNotNull(bgpFileWatcher.getWatchService());
- bgpFileWatcher.close();
+ try (DefaultFileWatcher bgpFileWatcher = new DefaultFileWatcher()) {
+ bgpFileWatcher.activate();
+
+ assertEquals(PATH, bgpFileWatcher.getPathFile());
+ assertNotNull(bgpFileWatcher.getWatchService());
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.config.loader.impl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class OSGiConfigLoaderTest {
+ @Mock
+ private FileWatcher watcher;
+ @Mock
+ private WatchService watchService;
+ @Mock
+ private WatchKey watchKey;
+ @Mock
+ private WatchEvent<?> watchEvent;
+ @Mock
+ private BindingDOMCodecServices codec;
+
+ private OSGiConfigLoader loader;
+
+ @Before
+ public void before() throws InterruptedException {
+ doReturn(watchService).when(watcher).getWatchService();
+ doReturn("foo").when(watcher).getPathFile();
+ doReturn(watchKey).when(watchService).take();
+ doAnswer(inv -> {
+ doThrow(new RuntimeException("enough!")).when(watchKey).pollEvents();
+ return List.of(watchEvent);
+ }).when(watchKey).pollEvents();
+ doReturn("watchEvent").when(watchEvent).context();
+ doReturn(true).when(watchKey).reset();
+
+ loader = new OSGiConfigLoader();
+ loader.watcher = watcher;
+ loader.codecServices = codec;
+ }
+
+ @After
+ public void after() {
+ loader.deactivate();
+ }
+
+ @Test
+ public void testSimpleConfigLoader() {
+ loader.activate();
+ verify(watchKey, timeout(10000)).reset();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.config.loader.impl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
+import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class SimpleConfigLoaderTest {
+ @Mock
+ private FileWatcher watcher;
+ @Mock
+ private WatchService watchService;
+ @Mock
+ private WatchKey watchKey;
+ @Mock
+ private WatchEvent<?> watchEvent;
+ @Mock
+ private BindingDOMCodecServices codec;
+ @Mock
+ private BindingRuntimeContext bindingContext;
+ @Mock
+ private EffectiveModelContext domContext;
+
+ private SimpleConfigLoader loader;
+
+ @Before
+ public void before() throws InterruptedException {
+ doReturn(bindingContext).when(codec).getRuntimeContext();
+ doReturn(domContext).when(bindingContext).getEffectiveModelContext();
+ doReturn(watchService).when(watcher).getWatchService();
+ doReturn("foo").when(watcher).getPathFile();
+ doReturn(watchKey).when(watchService).take();
+ doAnswer(inv -> {
+ doThrow(new RuntimeException("enough!")).when(watchKey).pollEvents();
+ return List.of(watchEvent);
+ }).when(watchKey).pollEvents();
+ doReturn("watchEvent").when(watchEvent).context();
+ doReturn(true).when(watchKey).reset();
+
+ loader = new SimpleConfigLoader(watcher, codec);
+ }
+
+ @After
+ public void after() {
+ loader.close();
+ }
+
+ @Test
+ public void testSimpleConfigLoader() {
+ loader.init();
+ verify(watchKey, timeout(10000)).reset();
+ }
+}
import static org.opendaylight.protocol.util.CheckUtil.checkNotPresentConfiguration;
import static org.opendaylight.protocol.util.CheckUtil.checkPresentConfiguration;
-import org.junit.After;
import org.junit.Before;
import org.opendaylight.bgpcep.config.loader.impl.AbstractConfigLoaderTest;
this.policyLoader.init();
checkPresentConfiguration(getDataBroker(), ROUTING_POLICY_IID);
this.policyLoader.close();
- this.configLoader.close();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
}
}
<type>xml</type>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>odl-controller-blueprint</artifactId>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>odl-yangtools-codec</artifactId>
<classifier>features</classifier>
<type>xml</type>
</dependency>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
<feature name="odl-bgpcep-config-loader-impl" version="${project.version}">
- <feature version="[3,4)">odl-controller-blueprint</feature>
+ <feature version="[6,7)">odl-yangtools-codec</feature>
</feature>
</features>