
org.freedesktop.dbus.connections.base.AbstractConnectionBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dbus-java-osgi Show documentation
Show all versions of dbus-java-osgi Show documentation
Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/).
This is the OSGi compliant bundle of all required libraries in one bundle.
The newest version!
package org.freedesktop.dbus.connections.base;
import org.freedesktop.dbus.DBusCallInfo;
import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.RemoteObject;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.IDisconnectAction;
import org.freedesktop.dbus.connections.IDisconnectCallback;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.connections.impl.ConnectionConfig;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.errors.UnknownProperty;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.FatalDBusException;
import org.freedesktop.dbus.exceptions.NotConnected;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.DBusSigHandler;
import org.freedesktop.dbus.messages.*;
import org.freedesktop.dbus.messages.Error;
import org.freedesktop.dbus.messages.constants.Flags;
import org.freedesktop.dbus.utils.IThrowingConsumer;
import org.freedesktop.dbus.utils.IThrowingFunction;
import org.freedesktop.dbus.utils.NameableThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
/**
* Class containing most parts required for a arbitrary connection.
* It is not intended to be used directly, therefore it is sealed.
*
* @since 5.0.0 - 2023-10-23
* @author hypfvieh
*/
public abstract sealed class AbstractConnectionBase implements Closeable permits ConnectionMethodInvocation {
private static final Map INFOMAP = new ConcurrentHashMap<>();
private final Logger logger;
private final ObjectTree objectTree;
private final Map exportedObjects;
private final Map importedObjects;
private final PendingCallbackManager callbackManager;
private final FallbackContainer fallbackContainer;
private final ExecutorService senderService;
private final ReceivingService receivingService;
private final IncomingMessageThread readerThread;
private final Map>> handledSignals;
private final Map>> genericHandledSignals;
private final Map pendingCalls;
private final Queue pendingErrorQueue;
private final BusAddress busAddress;
private final MessageFactory messageFactory;
private final ConnectionConfig connectionConfig;
private AbstractTransport transport;
private volatile boolean disconnecting;
protected AbstractConnectionBase(ConnectionConfig _conCfg, TransportConfig _transportConfig, ReceivingServiceConfig _rsCfg) throws DBusException {
logger = LoggerFactory.getLogger(getClass());
connectionConfig = Objects.requireNonNull(_conCfg, "Connection configuration required");
exportedObjects = Collections.synchronizedMap(new HashMap<>());
importedObjects = connectionConfig.isImportWeakReferences() ? Collections.synchronizedMap(new WeakHashMap<>()) : new ConcurrentHashMap<>();
doWithExportedObjects(DBusException.class, eos -> eos.put(null, new ExportedObject(new GlobalHandler(this), false)));
disconnecting = false;
handledSignals = new ConcurrentHashMap<>();
genericHandledSignals = new ConcurrentHashMap<>();
pendingCalls = Collections.synchronizedMap(new LinkedHashMap<>());
callbackManager = new PendingCallbackManager();
pendingErrorQueue = new ConcurrentLinkedQueue<>();
TransportBuilder transportBuilder = TransportBuilder.create(_transportConfig);
busAddress = transportBuilder.getAddress();
String senderThreadName = "DBus Sender Thread-";
String rcvSvcName = "";
if (logger.isDebugEnabled()) {
senderThreadName = "DBus Sender Thread: " + busAddress.isListeningSocket() + ", ";
rcvSvcName = "RcvSvc: " + busAddress.isListeningSocket() + " ";
}
receivingService = new ReceivingService(rcvSvcName, _rsCfg);
senderService =
Executors.newFixedThreadPool(1, new NameableThreadFactory(senderThreadName, true));
objectTree = new ObjectTree();
fallbackContainer = new FallbackContainer();
readerThread = Objects.requireNonNull(createReaderThread(busAddress), "Reader thread required");
try {
transport = transportBuilder.build();
messageFactory = Optional.ofNullable(transport)
.map(AbstractTransport::getMessageFactory)
.orElseThrow();
} catch (IOException | DBusException _ex) {
logger.debug("Error creating transport", _ex);
if (_ex instanceof IOException ioe) {
internalDisconnect(ioe);
}
throw new DBusException("Failed to connect to bus: " + _ex.getMessage(), _ex);
}
}
/**
* Retrieves an remote object using source and path.
* Will try to find suitable exported DBusInterface automatically.
*
* @param _source source
* @param _path path
*
* @return {@link DBusInterface} compatible object
*/
public abstract DBusInterface getExportedObject(String _source, String _path) throws DBusException;
/**
* Retrieves an remote object using source and path.
* Will use the given type as object class.
*
* @param _source source
* @param _path path
* @param _type class of remote object
*
* @return {@link DBusInterface} compatible object
*/
public abstract T getExportedObject(String _source, String _path, Class _type) throws DBusException;
/**
* Create the read thread for reading incoming messages.
*
* @param _busAddress current bus address
* @return IncomingMessageThread, never null
*/
protected abstract IncomingMessageThread createReaderThread(BusAddress _busAddress);
/**
* The generated UUID of this machine.
* @return String
*/
public abstract String getMachineId();
Message readIncoming() throws DBusException {
if (!isConnected()) {
return null;
}
Message m = null;
try {
m = getTransport().readMessage();
} catch (IOException _exIo) {
if (_exIo instanceof EOFException || _exIo instanceof ClosedByInterruptException) {
Optional.ofNullable(getDisconnectCallback()).ifPresent(IDisconnectCallback::clientDisconnect);
if (disconnecting // when we are already disconnecting, ignore further errors
|| getBusAddress().isListeningSocket()) { // when we are listener, a client may disconnect any time which
// is no error
return null;
}
}
if (isConnected()) {
throw new FatalDBusException(_exIo);
} // if run is false, suppress all exceptions - the connection either is already disconnected or should be disconnected right now
}
return m;
}
/**
* Disconnects the DBus session. This method is final as it should never be overwritten by subclasses, otherwise
* we have an endless recursion when using {@link #disconnect(IDisconnectAction, IDisconnectAction)} which then will
* cause a StackOverflowError.
*
* @param _connectionError exception caused the disconnection (null if intended disconnect)
*/
protected final synchronized void internalDisconnect(IOException _connectionError) {
if (!isConnected()) { // already disconnected
getLogger().debug("Ignoring disconnect, already disconnected");
return;
}
disconnecting = true;
getLogger().debug("Disconnecting Abstract Connection");
Optional.ofNullable(getDisconnectCallback()).ifPresent(cb ->
Optional.ofNullable(_connectionError)
.ifPresentOrElse(cb::disconnectOnError, () -> cb.requestedDisconnect(null))
);
getImportedObjects().clear();
// stop reading new messages
readerThread.terminate();
// terminate the signal handling pool
receivingService.shutdown(10, TimeUnit.SECONDS);
// stop potentially waiting method-calls
getLogger().debug("Notifying {} method call(s) to stop waiting for replies", getPendingCalls().size());
Exception interrupt = _connectionError == null ? new IOException("Disconnecting") : _connectionError;
for (MethodCall mthCall : getPendingCalls().values()) {
try {
mthCall.setReply(getMessageFactory().createError(mthCall, interrupt));
} catch (DBusException _ex) {
getLogger().debug("Cannot set method reply to error", _ex);
}
}
// shutdown sender executor service, send all remaining messages in main thread when no exception caused disconnection
getLogger().debug("Shutting down SenderService");
List remainingMsgsToSend = senderService.shutdownNow();
// only try to send remaining messages when disconnection was not
// caused by an IOException, otherwise we may block for method calls waiting for
// reply which will never be received (due to disconnection by IOException)
if (_connectionError == null) {
for (Runnable runnable : remainingMsgsToSend) {
runnable.run();
}
} else if (!remainingMsgsToSend.isEmpty()) {
getLogger().debug("Will not send {} messages due to connection closed by IOException", remainingMsgsToSend.size());
}
// disconnect from the transport layer
try {
if (transport != null) {
transport.close();
transport = null;
}
} catch (IOException _ex) {
getLogger().debug("Exception while disconnecting transport.", _ex);
}
// stop all the workers
receivingService.shutdownNow();
disconnecting = false;
}
/**
* Special disconnect method which may be used whenever some cleanup before or after
* disconnection to DBus is required.
* @param _before action execute before actual disconnect, null if not needed
* @param _after action execute after disconnect, null if not needed
*/
protected synchronized void disconnect(IDisconnectAction _before, IDisconnectAction _after) {
if (_before != null) {
_before.perform();
}
internalDisconnect(null);
if (_after != null) {
_after.perform();
}
}
/**
* Disconnect from the Bus.
*/
public synchronized void disconnect() {
getLogger().debug("Disconnect called");
internalDisconnect(null);
}
/**
* Sends a reply on the bus to signal a non-existing property was requested.
*
* @param _methodCall method call
* @param _params params
*
* @throws DBusException when sending fails
*/
protected void rejectUnknownProperty(final MethodCall _methodCall, Object[] _params) throws DBusException {
Object p = _params != null && _params.length >= 2 ? _params[1] : "unknown";
sendMessage(getMessageFactory().createError(_methodCall, new UnknownProperty(String.format(
"The property `%s' does not exist.", p))));
}
/**
* Do some action with the currently exported objects in a synchronized manor.
*
* @param _exClz exception type which may be thrown
* @param _action action to execute
* @param type of exception
*
* @return whatever the action returns
*
* @throws X thrown when action throws
*/
protected T doWithExportedObjectsAndReturn(Class _exClz, IThrowingFunction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy