/* * Copyright (c) 2016 Cisco Systems, Inc. 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.groupbasedpolicy.renderer.ios_xe_provider.impl.manager; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BinaryOperator; import org.apache.commons.lang3.tuple.MutablePair; import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.api.manager.PolicyManager; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** * Purpose: wrap {@link PolicyManager} with state compression mechanism */ public class PolicyManagerZipImpl implements PolicyManager { private static final Logger LOG = LoggerFactory.getLogger(PolicyManagerZipImpl.class); public static final BinaryOperator<ConfigPairBox> CONFIG_PAIR_ZIP_FUNCTION = new BinaryOperator<ConfigPairBox>() { @Override public ConfigPairBox apply(final ConfigPairBox configPairOld, final ConfigPairBox configPairNew) { final ConfigPairBox zippedConfigPair; if (configPairOld == null) { zippedConfigPair = configPairNew; } else { LOG.trace("zipping policy configuration"); configPairOld.setRight(configPairNew.getRight()); zippedConfigPair = configPairOld; } return zippedConfigPair; } }; public static final int POOL_SHUTDOWN_TIMEOUT = 10; private final PolicyManager delegate; private AtomicReference<ConfigPairBox> configPairKeeper; private ListeningExecutorService syncPool; public PolicyManagerZipImpl(final PolicyManager delegate) { this.delegate = Preconditions.checkNotNull(delegate, "missing PM delegate"); configPairKeeper = new AtomicReference<>(); syncPool = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("iosxe-renderer-zip-pool-%d").build()) { @Override protected void afterExecute(final Runnable r, final Throwable t) { if (t != null) { LOG.warn("renderer failed to execute config sync: ", t); } super.afterExecute(r, t); } }); } @Override public ListenableFuture<Boolean> syncPolicy(final Configuration dataBefore, final Configuration dataAfter, final long version) { LOG.trace("firing configuration zip"); // add config to zipping storage final ConfigPairBox configPair = new ConfigPairBox(dataBefore, dataAfter); final ConfigPairBox previousStoredConfig = storeConfig(configPair); // delegate execution - submit process task if storage contained no config for corresponding device final ListenableFuture<Boolean> result; if (previousStoredConfig == null) { LOG.trace("submitting task for delegating policy configuration"); result = Futures.dereference(syncPool.submit(new Callable<ListenableFuture<Boolean>>() { @Override public ListenableFuture<Boolean> call() throws Exception { final ConfigPairBox configPairBox = configPairKeeper.getAndSet(null); LOG.debug("delegating policy configuration"); return delegate.syncPolicy(configPairBox.getLeft(), configPair.getRight(), version); } })); } else { result = Futures.immediateFuture(true); } return result; } private ConfigPairBox storeConfig(ConfigPairBox configPair) { return configPairKeeper.getAndAccumulate(configPair, CONFIG_PAIR_ZIP_FUNCTION); } @Override public void close() { if (syncPool != null && !syncPool.isShutdown()) { syncPool.shutdown(); boolean terminated; try { terminated = syncPool.awaitTermination(POOL_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.warn("failed to shutdown processing pool", e); terminated = false; } if (!terminated) { syncPool.shutdownNow(); } } } private static class ConfigPairBox extends MutablePair<Configuration, Configuration> { public ConfigPairBox(final Configuration left, final Configuration right) { super(left, right); } } }