
org.ow2.petals.bc.gateway.inbound.ConsumerDomain Maven / Gradle / Ivy
/**
* Copyright (c) 2015-2016 Linagora
*
* This program/library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or (at your
* option) any later version.
*
* This program/library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program/library; If not, see http://www.gnu.org/licenses/
* for the GNU Lesser General Public License version 2.1.
*/
package org.ow2.petals.bc.gateway.inbound;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import javax.jbi.JBIException;
import javax.jbi.servicedesc.ServiceEndpoint;
import javax.xml.namespace.QName;
import org.eclipse.jdt.annotation.Nullable;
import org.ow2.petals.bc.gateway.JBISender;
import org.ow2.petals.bc.gateway.BcGatewaySUManager;
import org.ow2.petals.bc.gateway.commons.AbstractDomain;
import org.ow2.petals.bc.gateway.commons.messages.ServiceKey;
import org.ow2.petals.bc.gateway.commons.messages.TransportedDocument;
import org.ow2.petals.bc.gateway.commons.messages.TransportedMessage;
import org.ow2.petals.bc.gateway.commons.messages.TransportedPropagations;
import org.ow2.petals.bc.gateway.jbidescriptor.generated.JbiConsumerDomain;
import org.ow2.petals.bc.gateway.utils.BcGatewayConsumeExtFlowStepBeginLogData;
import org.ow2.petals.commons.log.FlowAttributes;
import org.ow2.petals.commons.log.Level;
import org.ow2.petals.commons.log.PetalsExecutionContext;
import org.ow2.petals.component.framework.api.exception.PEtALSCDKException;
import org.ow2.petals.component.framework.jbidescriptor.generated.Consumes;
import org.ow2.petals.component.framework.logger.StepLogHelper;
import org.ow2.petals.component.framework.su.ServiceUnitDataHandler;
import org.w3c.dom.Document;
import io.netty.channel.Channel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.ScheduledFuture;
/**
* There is one instance of this class per consumer domain in an SU configuration (jbi.xml).
*
* It is responsible of notifying the channels (to consumer partner) of existing Consumes propagated to them.
*
* The main idea is that a given consumer partner can contact us (a provider partner) with multiple connections (for
* example in case of HA) and each of these needs to know what are the consumes propagated to them.
*
* @author vnoel
*
*/
public class ConsumerDomain extends AbstractDomain {
/**
* The {@link Consumes} propagated to this consumer domain.
*/
private final Collection consumes;
private final BcGatewaySUManager sum;
private final TransportListener tl;
/**
* Lock for synchronising changes to {@link #channels}, {@link #open}, {@link #propagations} and {@link #jcd}.
*/
private final Lock mainLock = new ReentrantLock(true);
/**
* The channels from this consumer domain (there can be more than one in case of HA or stuffs like that for example)
*/
private final Set channels = new HashSet<>();
private JbiConsumerDomain jcd;
private volatile boolean open = false;
private TransportedPropagations propagations = TransportedPropagations.EMPTY;
/**
* @see #polling
*/
private final Lock pollingLock = new ReentrantLock();
/**
* Access is controlled by {@link #pollingLock} (except for the first poll that is controlled by {@link #mainLock} in
* {@link #open()}).
*/
private @Nullable ScheduledFuture> polling = null;
public ConsumerDomain(final ServiceUnitDataHandler handler, final TransportListener tl,
final BcGatewaySUManager sum, final JbiConsumerDomain jcd, final Collection consumes,
final JBISender sender, final Logger logger) throws PEtALSCDKException {
super(sender, handler, logger);
this.tl = tl;
this.sum = sum;
this.jcd = jcd;
this.consumes = consumes;
// Consumer partner will be able to connect to us (but no consumes will be propagated until open() is called)
tl.register(jcd.getAuthName(), this);
}
@Override
public String getId() {
final String id = jcd.getId();
assert id != null;
return id;
}
public void reload(final JbiConsumerDomain newJCD) throws PEtALSCDKException {
mainLock.lock();
try {
if (!jcd.getAuthName().equals(newJCD.getAuthName()) || !jcd.getCertificate().equals(newJCD.getCertificate())
|| !jcd.getRemoteCertificate().equals(newJCD.getRemoteCertificate())
|| !jcd.getKey().equals(newJCD.getKey()) || !jcd.getPassphrase().equals(newJCD.getPassphrase())) {
if (!jcd.getAuthName().equals(newJCD.getAuthName())) {
tl.register(newJCD.getAuthName(), this);
tl.deregistrer(jcd.getAuthName());
}
jcd = newJCD;
// this will disconnect clients and they should reconnect by themselves and use the new jcd and
// authname!
disconnect();
}
} finally {
mainLock.unlock();
}
}
public JbiConsumerDomain getJCD() {
return jcd;
}
/**
* Consumer partner will be disconnected
*/
public void disconnect() {
mainLock.lock();
try {
tl.deregistrer(jcd.getAuthName());
for (final Channel c : channels) {
// this will trigger deregisterChannel btw
// Note: locking is ok, simply all the close will be sent before the deregistration is executed
c.close();
}
} finally {
mainLock.unlock();
}
}
public void open() {
mainLock.lock();
try {
open = true;
sendPropagations(true);
final long propagationPollingMaxDelay = jcd.getPropagationPollingMaxDelay();
if (propagationPollingMaxDelay > 0) {
final double propagationPollingAccel = jcd.getPropagationPollingAcceleration();
if (logger.isLoggable(Level.CONFIG)) {
logger.config(
"Propagation refresh polling is enabled (max delay: " + propagationPollingMaxDelay
+ "ms, acceleration: " + propagationPollingAccel + ")");
}
scheduleNextPolling(5000, propagationPollingAccel, propagationPollingMaxDelay);
} else {
logger.config("Propagation refresh polling is disabled");
}
} finally {
mainLock.unlock();
}
}
private void scheduleNextPolling(final long currentDelay, final double accel, final long maxDelay) {
final Runnable command = new Runnable() {
@Override
public void run() {
final long nextDelay;
if (accel > 1) {
nextDelay = Math.min((long) (currentDelay * accel), maxDelay);
} else {
nextDelay = maxDelay;
}
try {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Propagation refresh polling (next in " + nextDelay + "ms)");
}
// TODO catch exceptions?!
if (sendPropagations(false)) {
logger.info("Changes in propagations detected: refreshed!");
}
} finally {
try {
// in case it was interrupted during the propagation sending
// this will also reset the interrupted flag
pollingLock.lockInterruptibly();
try {
// polling corresponds to the current task
// if it's null, it was cancelled (thus the test for
// isCancelled is not really needed but well...)
if (polling != null && !polling.isCancelled()) {
scheduleNextPolling(nextDelay, accel, maxDelay);
}
} finally {
pollingLock.unlock();
}
} catch (final InterruptedException e) {
// we were interrupted, it's ok, we stop there
}
}
}
};
final long delay;
if (accel > 1) {
delay = Math.min(currentDelay, maxDelay);
} else {
delay = maxDelay;
}
polling = (ScheduledFuture>) GlobalEventExecutor.INSTANCE.schedule(command, delay, TimeUnit.MILLISECONDS)
.addListener(new FutureListener
© 2015 - 2025 Weber Informatics LLC | Privacy Policy