Upgrade dbus-java to 4.3.0. Incorporated: dbus-java-core and dbus-java-transport-native-unixsocket

This commit is contained in:
rmartinc 2023-05-05 09:00:00 +02:00 committed by Marek Posolda
parent 5aef59acd8
commit 87905c186d
193 changed files with 17165 additions and 9821 deletions

View file

@ -1,53 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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 cx.ath.matthew;
/**
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>.
*/
public class LibraryLoader {
private static final String[] PATHS = {
"/opt/rh/rh-sso7/root/lib/",
"/opt/rh/rh-sso7/root/lib64/",
"/usr/lib/",
"/usr/lib64/",
"/usr/local/lib/",
"/opt/local/lib/"
};
private static final String LIBRARY_NAME = "libunix_dbus_java";
private static final String VERSION = "0.0.8";
private static boolean loadSucceeded;
public static LibraryLoader load() {
for (String path : PATHS) {
try {
System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION));
loadSucceeded = true;
break;
} catch (UnsatisfiedLinkError e) {
loadSucceeded = false;
}
}
return new LibraryLoader();
}
public boolean succeed() {
return loadSucceeded;
}
}

View file

@ -1,673 +0,0 @@
/* Copyright (C) 1991-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C 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.
The GNU C 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 the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* This header is separate from features.h so that the compiler can
include it implicitly at the start of every compilation. It must
not itself include <features.h> or any other header that includes
<features.h> because the implicit include comes before any feature
test macros that may be defined in a source file before it first
explicitly includes a system header. GCC knows the name of this
header in order to preinclude it. */
/* glibc's intent is to support the IEC 559 math functionality, real
and complex. If the GCC (4.9 and later) predefined macros
specifying compiler intent are available, use them to determine
whether the overall intent is to support these features; otherwise,
presume an older compiler has intent to support these features and
define these macros by default. */
/* wchar_t uses Unicode 7.0.0. Version 7.0 of the Unicode Standard is
synchronized with ISO/IEC 10646:2012, plus Amendments 1 (published
on April, 2013) and 2 (not yet published as of February, 2015).
Additionally, it includes the accelerated publication of U+20BD
RUBLE SIGN. Therefore Unicode 7.0.0 is between 10646:2012 and
10646:2014, and so we use the date ISO/IEC 10646:2012 Amd.1 was
published. */
/* We do not support C11 <threads.h>. */
/*
* Java Debug Library
*
* Copyright (c) Matthew Johnson 2005
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.debug;
import cx.ath.matthew.utils.Hexdump;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* Add debugging to your program, has support for large projects with multiple
* classes and debug levels per class. Supports optional enabling of debug
* per-level per-class and debug targets of files, Streams or stderr.
* Also supports timing between debug outputs, printing of stack traces for Throwables
* and files/line numbers on each message.
* <p>
* Debug now automatically figures out which class it was called from, so all
* methods passing in the calling class are deprecated.
* </p>
* <p>
* The defaults are to print all messages to stderr with class and method name.
* </p>
* <p>
* Should be called like this:
* </p>
* <pre>
* if (Debug.debug) Debug.print(Debug.INFO, "Debug Message");
* </pre>
*/
public class Debug {
/**
* This interface can be used to provide custom printing filters
* for certain classes.
*/
public static interface FilterCommand {
/**
* Called to print debug messages with a custom filter.
*
* @param output The PrintStream to output to.
* @param level The debug level of this message.
* @param location The textual location of the message.
* @param extra Extra information such as timing details.
* @param message The debug message.
* @param lines Other lines of a multiple-line debug message.
*/
public void filter(PrintStream output, int level, String location, String extra, String message, String[] lines);
}
/**
* Highest priority messages
*/
public static final int CRIT = 1;
/**
* Error messages
*/
public static final int ERR = 2;
/**
* Warnings
*/
public static final int WARN = 3;
/**
* Information
*/
public static final int INFO = 4;
/**
* Debug messages
*/
public static final int DEBUG = 5;
/**
* Verbose debug messages
*/
public static final int VERBOSE = 6;
/**
* Set this to false to disable compilation of Debug statements
*/
public static final boolean debug = false;
/**
* The current output stream (defaults to System.err)
*/
public static PrintStream debugout = System.err;
private static Properties prop = null;
private static boolean timing = false;
private static boolean ttrace = false;
private static boolean lines = false;
private static boolean hexdump = false;
private static long last = 0;
private static int balen = 36;
private static int bawidth = 80;
private static Class saveclass = null;
//TODO: 1.5 private static Map<Class<? extends Object>, FilterCommand> filterMap = new HashMap<Class<? extends Object>, FilterCommand>();
private static Map filterMap = new HashMap();
/**
* Set properties to configure debugging.
* Format of properties is class =&gt; level, e.g.
* <pre>
* cx.ath.matthew.io.TeeOutputStream = INFO
* cx.ath.matthew.io.DOMPrinter = DEBUG
* </pre>
* The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which
* correspond to all messages up to that level. The special words YES, ALL and TRUE
* cause all messages to be printed regardless of level. All other terms disable
* messages for that class. CRIT and ERR messages are always printed if debugging is enabled
* unless explicitly disabled.
* The special class name ALL can be used to set the default level for all classes.
*
* @param prop Properties object to use.
*/
public static void setProperties(Properties prop) {
Debug.prop = prop;
}
/**
* Read which class to debug on at which level from the given File.
* Syntax the same as Java Properties files:
* <pre>
* &lt;class&gt; = &lt;debuglevel&gt;
* </pre>
* E.G.
* <pre>
* cx.ath.matthew.io.TeeOutputStream = INFO
* cx.ath.matthew.io.DOMPrinter = DEBUG
* </pre>
* The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which
* correspond to all messages up to that level. The special words YES, ALL and TRUE
* cause all messages to be printed regardless of level. All other terms disable
* messages for that class. CRIT and ERR messages are always printed if debugging is enabled
* unless explicitly disabled.
* The special class name ALL can be used to set the default level for all classes.
*
* @param f File to read from.
*/
public static void loadConfig(File f) throws IOException {
prop = new Properties();
try (FileInputStream is = new FileInputStream((f))) {
prop.load(is);
}
}
/**
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static boolean debugging(Class c, int loglevel) {
if (debug) {
if (null == c) return true;
return debugging(c.getName(), loglevel);
}
return false;
}
public static boolean debugging(String s, int loglevel) {
if (debug) {
try {
if (null == s) return true;
if (null == prop) return loglevel <= DEBUG;
String d = prop.getProperty(s);
if (null == d || "".equals(d)) d = prop.getProperty("ALL");
if (null == d) return loglevel <= ERR;
if ("".equals(d)) return loglevel <= ERR;
d = d.toLowerCase();
if ("true".equals(d)) return true;
if ("yes".equals(d)) return true;
if ("all".equals(d)) return true;
if ("verbose".equals(d)) return loglevel <= VERBOSE;
if ("debug".equals(d)) return loglevel <= DEBUG;
if ("info".equals(d)) return loglevel <= INFO;
if ("warn".equals(d)) return loglevel <= WARN;
if ("err".equals(d)) return loglevel <= ERR;
if ("crit".equals(d)) return loglevel <= CRIT;
int i = Integer.parseInt(d);
return i >= loglevel;
} catch (Exception e) {
return false;
}
}
return false;
}
/**
* Output to the given Stream
*/
public static void setOutput(PrintStream p) throws IOException {
debugout = p;
}
/**
* Output to the given file
*/
public static void setOutput(String filename) throws IOException {
debugout = new PrintStream(new FileOutputStream(filename, true));
}
/**
* Output to the default debug.log
*/
public static void setOutput() throws IOException {
setOutput("./debug.log");
}
/**
* Log at DEBUG
*
* @param d The object to log
*/
public static void print(Object d) {
if (debug) {
if (d instanceof String)
print(DEBUG, (String) d);
else if (d instanceof Throwable)
print(DEBUG, (Throwable) d);
else if (d instanceof byte[])
print(DEBUG, (byte[]) d);
else if (d instanceof Map)
printMap(DEBUG, (Map) d);
else print(DEBUG, d);
}
}
/**
* Log at DEBUG
*
* @param o The object doing the logging
* @param d The object to log
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Object o, Object d) {
if (debug) {
if (o instanceof Class)
saveclass = (Class) o;
else
saveclass = o.getClass();
print(d);
}
}
/**
* Log an Object
*
* @param o The object doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param d The object to log with d.toString()
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Object o, int loglevel, Object d) {
if (debug) {
if (o instanceof Class)
saveclass = (Class) o;
else
saveclass = o.getClass();
print(loglevel, d);
}
}
/**
* Log a String
*
* @param o The object doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param s The log message
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Object o, int loglevel, String s) {
if (debug) {
if (o instanceof Class)
saveclass = (Class) o;
else
saveclass = o.getClass();
print(loglevel, s);
}
}
/**
* Log a Throwable
*
* @param o The object doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param t The throwable to log with .toString and .printStackTrace
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Object o, int loglevel, Throwable t) {
if (debug) {
if (o instanceof Class)
saveclass = (Class) o;
else
saveclass = o.getClass();
print(loglevel, t);
}
}
/**
* Log a Throwable
*
* @param c The class doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param t The throwable to log with .toString and .printStackTrace
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Class c, int loglevel, Throwable t) {
if (debug) {
saveclass = c;
print(loglevel, t);
}
}
/**
* Log a Throwable
*
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param t The throwable to log with .toString and .printStackTrace
* @see #setThrowableTraces to turn on stack traces.
*/
public static void print(int loglevel, Throwable t) {
if (debug) {
String timestr = "";
String[] data = getTraceElements();
if (debugging(data[0], loglevel)) {
if (timing) {
long now = System.currentTimeMillis();
timestr = "{" + (now - last) + "} ";
last = now;
}
String[] lines = null;
if (ttrace) {
StackTraceElement[] ste = t.getStackTrace();
lines = new String[ste.length];
for (int i = 0; i < ste.length; i++)
lines[i] = "\tat " + ste[i].toString();
}
_print(t.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, t.toString(), lines);
}
}
}
/**
* Log a byte array
*
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param b The byte array to print.
* @see #setHexDump to enable hex dumping.
* @see #setByteArrayCount to change how many bytes are printed.
* @see #setByteArrayWidth to change the formatting width of hex.
*/
public static void print(int loglevel, byte[] b) {
if (debug) {
String timestr = "";
String[] data = getTraceElements();
if (debugging(data[0], loglevel)) {
if (timing) {
long now = System.currentTimeMillis();
timestr = "{" + (now - last) + "} ";
last = now;
}
String[] lines = null;
if (hexdump) {
if (balen >= b.length)
lines = Hexdump.format(b, bawidth).split("\n");
else {
byte[] buf = new byte[balen];
System.arraycopy(b, 0, buf, 0, balen);
lines = Hexdump.format(buf, bawidth).split("\n");
}
}
_print(b.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, b.length + " bytes", lines);
}
}
}
/**
* Log a String
*
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param s The string to log with d.toString()
*/
public static void print(int loglevel, String s) {
if (debug)
print(loglevel, (Object) s);
}
/**
* Log an Object
*
* @param c The class doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param d The object to log with d.toString()
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Class c, int loglevel, Object d) {
if (debug) {
saveclass = c;
print(loglevel, d);
}
}
/**
* Log a String
*
* @param c The class doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param s The log message
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void print(Class c, int loglevel, String s) {
if (debug) {
saveclass = c;
print(loglevel, s);
}
}
private static String[] getTraceElements() {
String[] data = new String[]{"", "", ""};
try {
Method m = Thread.class.getMethod("getStackTrace", new Class[0]);
StackTraceElement[] stes = (StackTraceElement[]) m.invoke(Thread.currentThread(), new Object[0]);
for (StackTraceElement ste : stes) {
if (Debug.class.getName().equals(ste.getClassName())) continue;
if (Thread.class.getName().equals(ste.getClassName())) continue;
if (Method.class.getName().equals(ste.getClassName())) continue;
if (ste.getClassName().startsWith("sun.reflect")) continue;
data[0] = ste.getClassName();
data[1] = ste.getMethodName();
if (lines)
data[2] = " " + ste.getFileName() + ":" + ste.getLineNumber();
break;
}
} catch (NoSuchMethodException NSMe) {
if (null != saveclass)
data[0] = saveclass.getName();
} catch (IllegalAccessException IAe) {
} catch (InvocationTargetException ITe) {
}
return data;
}
/**
* Log an Object
*
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param o The object to log
*/
public static void print(int loglevel, Object o) {
if (debug) {
String timestr = "";
String[] data = getTraceElements();
if (debugging(data[0], loglevel)) {
if (timing) {
long now = System.currentTimeMillis();
timestr = "{" + (now - last) + "} ";
last = now;
}
_print(o.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, o.toString(), null);
}
}
}
/**
* Log a Map
*
* @param o The object doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param m The Map to print out
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void printMap(Object o, int loglevel, Map m) {
if (debug) {
if (o instanceof Class)
saveclass = (Class) o;
else
saveclass = o.getClass();
printMap(loglevel, m);
}
}
/**
* Log a Map
*
* @param c The class doing the logging
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param m The Map to print out
* @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in.
*/
//TODO: 1.5 @Deprecated()
public static void printMap(Class c, int loglevel, Map m) {
if (debug) {
saveclass = c;
printMap(loglevel, m);
}
}
/**
* Log a Map at DEBUG log level
*
* @param m The Map to print out
*/
public static void printMap(Map m) {
printMap(DEBUG, m);
}
/**
* Log a Map
*
* @param loglevel The level to log at (DEBUG, WARN, etc)
* @param m The Map to print out
*/
public static void printMap(int loglevel, Map m) {
if (debug) {
String timestr = "";
String[] data = getTraceElements();
if (debugging(data[0], loglevel)) {
if (timing) {
long now = System.currentTimeMillis();
timestr = "{" + (now - last) + "} ";
last = now;
}
Iterator i = m.keySet().iterator();
String[] lines = new String[m.size()];
int j = 0;
while (i.hasNext()) {
Object key = i.next();
lines[j++] = "\t\t- " + key + " => " + m.get(key);
}
_print(m.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, "Map:", lines);
}
}
}
/**
* Enable or disable stack traces in Debuging throwables.
*/
public static void setThrowableTraces(boolean ttrace) {
Debug.ttrace = ttrace;
}
/**
* Enable or disable timing in Debug messages.
*/
public static void setTiming(boolean timing) {
Debug.timing = timing;
}
/**
* Enable or disable line numbers.
*/
public static void setLineNos(boolean lines) {
Debug.lines = lines;
}
/**
* Enable or disable hexdumps.
*/
public static void setHexDump(boolean hexdump) {
Debug.hexdump = hexdump;
}
/**
* Set the size of hexdumps.
* (Default: 36)
*/
public static void setByteArrayCount(int count) {
Debug.balen = count;
}
/**
* Set the formatted width of hexdumps.
* (Default: 80 chars)
*/
public static void setByteArrayWidth(int width) {
Debug.bawidth = width;
}
/**
* Add a filter command for a specific type.
* This command will be called with the output stream
* and the text to be sent. It should perform any
* changes necessary to the text and then print the
* result to the output stream.
*/
public static void addFilterCommand(Class c, FilterCommand f)
//TODO 1.5: public static void addFilterCommand(Class<? extends Object> c, FilterCommand f)
{
filterMap.put(c, f);
}
private static void _print(Class c, int level, String loc, String extra, String message, String[] lines) {
//TODO 1.5: FilterCommand f = filterMap.get(c);
FilterCommand f = (FilterCommand) filterMap.get(c);
if (null == f) {
debugout.println("[" + loc + "] " + extra + message);
if (null != lines)
for (String s : lines)
debugout.println(s);
} else
f.filter(debugout, level, loc, extra, message, lines);
}
}

View file

@ -1,35 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
import java.net.SocketException;
public class NotConnectedException extends SocketException {
public NotConnectedException() {
super("The Socket is Not Connected");
}
}

View file

@ -1,94 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
import java.io.IOException;
import java.io.InputStream;
public class USInputStream extends InputStream {
public static final int MSG_DONTWAIT = 0x40;
private native int native_recv(int sock, byte[] b, int off, int len, int flags, int timeout) throws IOException;
private int sock;
boolean closed = false;
private byte[] onebuf = new byte[1];
private UnixSocket us;
private boolean blocking = true;
private int flags = 0;
private int timeout = 0;
public USInputStream(int sock, UnixSocket us) {
this.sock = sock;
this.us = us;
}
public void close() throws IOException {
closed = true;
us.close();
}
public boolean markSupported() {
return false;
}
public int read() throws IOException {
int rv = 0;
while (0 >= rv) rv = read(onebuf);
if (-1 == rv) return -1;
return 0 > onebuf[0] ? -onebuf[0] : onebuf[0];
}
public int read(byte[] b, int off, int len) throws IOException {
if (closed) throw new NotConnectedException();
int count = native_recv(sock, b, off, len, flags, timeout);
/* Yes, I really want to do this. Recv returns 0 for 'connection shut down'.
* read() returns -1 for 'end of stream.
* Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised)
* whereas read() returns 0 for '0 bytes read', so yes, I really want to swap them here.
*/
if (0 == count) return -1;
else if (-1 == count) return 0;
else return count;
}
public boolean isClosed() {
return closed;
}
public UnixSocket getSocket() {
return us;
}
public void setBlocking(boolean enable) {
flags = enable ? 0 : MSG_DONTWAIT;
}
public void setSoTimeout(int timeout) {
this.timeout = timeout;
}
}

View file

@ -1,78 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
import java.io.IOException;
import java.io.OutputStream;
public class USOutputStream extends OutputStream {
private native int native_send(int sock, byte[] b, int off, int len) throws IOException;
private native int native_send(int sock, byte[][] b) throws IOException;
private int sock;
boolean closed = false;
private byte[] onebuf = new byte[1];
private UnixSocket us;
public USOutputStream(int sock, UnixSocket us) {
this.sock = sock;
this.us = us;
}
public void close() throws IOException {
closed = true;
us.close();
}
public void flush() {
} // no-op, we do not buffer
public void write(byte[][] b) throws IOException {
if (closed) throw new NotConnectedException();
native_send(sock, b);
}
public void write(byte[] b, int off, int len) throws IOException {
if (closed) throw new NotConnectedException();
native_send(sock, b, off, len);
}
public void write(int b) throws IOException {
onebuf[0] = (byte) (b % 0x7F);
if (1 == (b % 0x80)) onebuf[0] = (byte) -onebuf[0];
write(onebuf);
}
public boolean isClosed() {
return closed;
}
public UnixSocket getSocket() {
return us;
}
}

View file

@ -1,43 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
import java.io.IOException;
/**
* An IO Exception which occurred during UNIX Socket IO
*/
public class UnixIOException extends IOException {
private int no;
private String message;
public UnixIOException(int no, String message) {
super(message);
this.message = message;
this.no = no;
}
}

View file

@ -1,346 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
import cx.ath.matthew.debug.Debug;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Represents a UnixSocket.
*/
public class UnixSocket {
private native void native_set_pass_cred(int sock, boolean passcred) throws IOException;
private native int native_connect(String address, boolean abs) throws IOException;
private native void native_close(int sock) throws IOException;
private native int native_getPID(int sock);
private native int native_getUID(int sock);
private native int native_getGID(int sock);
private native void native_send_creds(int sock, byte data) throws IOException;
private native byte native_recv_creds(int sock, int[] creds) throws IOException;
private UnixSocketAddress address = null;
private USOutputStream os = null;
private USInputStream is = null;
private boolean closed = false;
private boolean connected = false;
private boolean passcred = false;
private int sock = 0;
private boolean blocking = true;
private int uid = -1;
private int pid = -1;
private int gid = -1;
UnixSocket(int sock, UnixSocketAddress address) {
this.sock = sock;
this.address = address;
this.connected = true;
this.os = new USOutputStream(sock, this);
this.is = new USInputStream(sock, this);
}
/**
* Create an unconnected socket.
*/
public UnixSocket() {
}
/**
* Create a socket connected to the given address.
*
* @param address The Unix Socket address to connect to
*/
public UnixSocket(UnixSocketAddress address) throws IOException {
connect(address);
}
/**
* Create a socket connected to the given address.
*
* @param address The Unix Socket address to connect to
*/
public UnixSocket(String address) throws IOException {
this(new UnixSocketAddress(address));
}
/**
* Connect the socket to this address.
*
* @param address The Unix Socket address to connect to
*/
public void connect(UnixSocketAddress address) throws IOException {
if (connected) close();
this.sock = native_connect(address.path, address.abs);
this.os = new USOutputStream(this.sock, this);
this.is = new USInputStream(this.sock, this);
this.address = address;
this.connected = true;
this.closed = false;
this.is.setBlocking(blocking);
}
/**
* Connect the socket to this address.
*
* @param address The Unix Socket address to connect to
*/
public void connect(String address) throws IOException {
connect(new UnixSocketAddress(address));
}
public void finalize() {
try {
close();
} catch (IOException IOe) {
}
}
/**
* Closes the connection.
*/
public synchronized void close() throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Closing socket");
native_close(sock);
sock = 0;
this.closed = true;
this.connected = false;
os = null;
is = null;
}
/**
* Returns an InputStream for reading from the socket.
*
* @return An InputStream connected to this socket.
*/
public InputStream getInputStream() {
return is;
}
/**
* Returns an OutputStream for writing to the socket.
*
* @return An OutputStream connected to this socket.
*/
public OutputStream getOutputStream() {
return os;
}
/**
* Returns the address this socket is connected to.
* Returns null if the socket is unconnected.
*
* @return The UnixSocketAddress the socket is connected to
*/
public UnixSocketAddress getAddress() {
return address;
}
/**
* Send a single byte of data with credentials.
* (Works on BSDs)
*
* @param data The byte of data to send.
*/
public void sendCredentialByte(byte data) throws IOException {
if (!connected) throw new NotConnectedException();
native_send_creds(sock, data);
}
/**
* Receive a single byte of data, with credentials.
* (Works on BSDs)
*
* @param data The byte of data to send.
* @see getPeerUID
* @see getPeerPID
* @see getPeerGID
*/
public byte recvCredentialByte() throws IOException {
if (!connected) throw new NotConnectedException();
int[] creds = new int[]{-1, -1, -1};
byte data = native_recv_creds(sock, creds);
pid = creds[0];
uid = creds[1];
gid = creds[2];
return data;
}
/**
* Get the credential passing status.
* (only effective on linux)
*
* @return The current status of credential passing.
* @see setPassCred
*/
public boolean getPassCred() {
return passcred;
}
/**
* Return the uid of the remote process.
* Some data must have been received on the socket to do this.
* Either setPassCred must be called on Linux first, or recvCredentialByte
* on BSD.
*
* @return the UID or -1 if it is not available
*/
public int getPeerUID() {
if (-1 == uid)
uid = native_getUID(sock);
return uid;
}
/**
* Return the gid of the remote process.
* Some data must have been received on the socket to do this.
* Either setPassCred must be called on Linux first, or recvCredentialByte
* on BSD.
*
* @return the GID or -1 if it is not available
*/
public int getPeerGID() {
if (-1 == gid)
gid = native_getGID(sock);
return gid;
}
/**
* Return the pid of the remote process.
* Some data must have been received on the socket to do this.
* Either setPassCred must be called on Linux first, or recvCredentialByte
* on BSD.
*
* @return the PID or -1 if it is not available
*/
public int getPeerPID() {
if (-1 == pid)
pid = native_getPID(sock);
return pid;
}
/**
* Set the credential passing status.
* (Only does anything on linux, for other OS, you need
* to use send/recv credentials)
*
* @param enable Set to true for credentials to be passed.
*/
public void setPassCred(boolean enable) throws IOException {
native_set_pass_cred(sock, enable);
passcred = enable;
}
/**
* Get the blocking mode.
*
* @return true if reads are blocking.
* @see setBlocking
*/
public boolean getBlocking() {
return blocking;
}
/**
* Set the blocking mode.
*
* @param enable Set to false for non-blocking reads.
*/
public void setBlocking(boolean enable) {
blocking = enable;
if (null != is) is.setBlocking(enable);
}
/**
* Check the socket status.
*
* @return true if closed.
*/
public boolean isClosed() {
return closed;
}
/**
* Check the socket status.
*
* @return true if connected.
*/
public boolean isConnected() {
return connected;
}
/**
* Check the socket status.
*
* @return true if the input stream has been shutdown
*/
public boolean isInputShutdown() {
return is.isClosed();
}
/**
* Check the socket status.
*
* @return true if the output stream has been shutdown
*/
public boolean isOutputShutdown() {
return os.isClosed();
}
/**
* Shuts down the input stream.
* Subsequent reads on the associated InputStream will fail.
*/
public void shutdownInput() {
is.closed = true;
}
/**
* Shuts down the output stream.
* Subsequent writes to the associated OutputStream will fail.
*/
public void shutdownOutput() {
os.closed = true;
}
/**
* Set timeout of read requests.
*/
public void setSoTimeout(int timeout) {
is.setSoTimeout(timeout);
}
}

View file

@ -1,86 +0,0 @@
/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2004
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.unix;
/**
* Represents an address for a Unix Socket
*/
public class UnixSocketAddress {
String path;
boolean abs;
/**
* Create the address.
*
* @param path The path to the Unix Socket.
* @param abs True if this should be an abstract socket.
*/
public UnixSocketAddress(String path, boolean abs) {
this.path = path;
this.abs = abs;
}
/**
* Create the address.
*
* @param path The path to the Unix Socket.
*/
public UnixSocketAddress(String path) {
this.path = path;
this.abs = false;
}
/**
* Return the path.
*/
public String getPath() {
return path;
}
/**
* Returns true if this an address for an abstract socket.
*/
public boolean isAbstract() {
return abs;
}
/**
* Return the Address as a String.
*/
public String toString() {
return "unix" + (abs ? ":abstract" : "") + ":path=" + path;
}
public boolean equals(Object o) {
if (!(o instanceof UnixSocketAddress)) return false;
return ((UnixSocketAddress) o).path.equals(this.path);
}
public int hashCode() {
return path.hashCode();
}
}

View file

@ -1,147 +0,0 @@
/*
* Java Hexdump Library
*
* Copyright (c) Matthew Johnson 2005
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
package cx.ath.matthew.utils;
import java.io.PrintStream;
public class Hexdump {
public static final char[] hexchars = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String toHex(byte[] buf) {
return toHex(buf, 0, buf.length);
}
public static String toHex(byte[] buf, int ofs, int len) {
StringBuffer sb = new StringBuffer();
int j = ofs + len;
for (int i = ofs; i < j; i++) {
if (i < buf.length) {
sb.append(hexchars[(buf[i] & 0xF0) >> 4]);
sb.append(hexchars[buf[i] & 0x0F]);
sb.append(' ');
} else {
sb.append(' ');
sb.append(' ');
sb.append(' ');
}
}
return sb.toString();
}
public static String toAscii(byte[] buf) {
return toAscii(buf, 0, buf.length);
}
public static String toAscii(byte[] buf, int ofs, int len) {
StringBuffer sb = new StringBuffer();
int j = ofs + len;
for (int i = ofs; i < j; i++) {
if (i < buf.length) {
if (20 <= buf[i] && 126 >= buf[i])
sb.append((char) buf[i]);
else
sb.append('.');
} else
sb.append(' ');
}
return sb.toString();
}
public static String format(byte[] buf) {
return format(buf, 80);
}
public static String format(byte[] buf, int width) {
int bs = (width - 8) / 4;
int i = 0;
StringBuffer sb = new StringBuffer();
do {
for (int j = 0; j < 6; j++) {
sb.append(hexchars[(i << (j * 4) & 0xF00000) >> 20]);
}
sb.append('\t');
sb.append(toHex(buf, i, bs));
sb.append(' ');
sb.append(toAscii(buf, i, bs));
sb.append('\n');
i += bs;
} while (i < buf.length);
return sb.toString();
}
public static void print(byte[] buf) {
print(buf, System.err);
}
public static void print(byte[] buf, int width) {
print(buf, width, System.err);
}
public static void print(byte[] buf, int width, PrintStream out) {
out.print(format(buf, width));
}
public static void print(byte[] buf, PrintStream out) {
out.print(format(buf));
}
/**
* Returns a string which can be written to a Java source file as part
* of a static initializer for a byte array.
* Returns data in the format 0xAB, 0xCD, ....
* use like:
* javafile.print("byte[] data = {")
* javafile.print(Hexdump.toByteArray(data));
* javafile.println("};");
*/
public static String toByteArray(byte[] buf) {
return toByteArray(buf, 0, buf.length);
}
/**
* Returns a string which can be written to a Java source file as part
* of a static initializer for a byte array.
* Returns data in the format 0xAB, 0xCD, ....
* use like:
* javafile.print("byte[] data = {")
* javafile.print(Hexdump.toByteArray(data));
* javafile.println("};");
*/
public static String toByteArray(byte[] buf, int ofs, int len) {
StringBuffer sb = new StringBuffer();
for (int i = ofs; i < len && i < buf.length; i++) {
sb.append('0');
sb.append('x');
sb.append(hexchars[(buf[i] & 0xF0) >> 4]);
sb.append(hexchars[buf[i] & 0x0F]);
if ((i + 1) < len && (i + 1) < buf.length)
sb.append(',');
}
return sb.toString();
}
}

View file

@ -1,534 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop;
import org.freedesktop.dbus.DBusInterface;
import org.freedesktop.dbus.DBusSignal;
import org.freedesktop.dbus.Position;
import org.freedesktop.dbus.Struct;
import org.freedesktop.dbus.Tuple;
import org.freedesktop.dbus.UInt16;
import org.freedesktop.dbus.UInt32;
import org.freedesktop.dbus.UInt64;
import org.freedesktop.dbus.Variant;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
public interface DBus extends DBusInterface {
String BUSNAME = "org.freedesktop.DBus";
String OBJECTPATH = "/org/freedesktop/DBus";
int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01;
int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02;
int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04;
int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1;
int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2;
int DBUS_REQUEST_NAME_REPLY_EXISTS = 3;
int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4;
int DBUS_RELEASEME_REPLY_RELEASED = 1;
int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2;
int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3;
int DBUS_START_REPLY_SUCCESS = 1;
int DBUS_START_REPLY_ALREADY_RUNNING = 2;
/**
* All DBus Applications should respond to the Ping method on this interface
*/
public interface Peer extends DBusInterface {
public void Ping();
}
/**
* Objects can provide introspection data via this interface and method.
* See the <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format">Introspection Format</a>.
*/
public interface Introspectable extends DBusInterface {
/**
* @return The XML introspection data for this object
*/
public String Introspect();
}
/**
* A standard properties interface.
*/
public interface Properties extends DBusInterface {
/**
* Get the value for the given property.
*
* @param interface_name The interface this property is associated with.
* @param property_name The name of the property.
* @return The value of the property (may be any valid DBus type).
*/
public <A> A Get(String interface_name, String property_name);
/**
* Set the value for the given property.
*
* @param interface_name The interface this property is associated with.
* @param property_name The name of the property.
* @param value The new value of the property (may be any valid DBus type).
*/
public <A> void Set(String interface_name, String property_name, A value);
/**
* Get all properties and values.
*
* @param interface_name The interface the properties is associated with.
* @return The properties mapped to their values.
*/
public Map<String, Variant> GetAll(String interface_name);
}
/**
* Messages generated locally in the application.
*/
public interface Local extends DBusInterface {
public class Disconnected extends DBusSignal {
public Disconnected(String path) throws DBusException {
super(path);
}
}
}
/**
* Initial message to register ourselves on the Bus.
*
* @return The unique name of this connection to the Bus.
*/
public String Hello();
/**
* Lists all connected names on the Bus.
*
* @return An array of all connected names.
*/
public String[] ListNames();
/**
* Determine if a name has an owner.
*
* @param name The name to query.
* @return true if the name has an owner.
*/
public boolean NameHasOwner(String name);
/**
* Get the connection unique name that owns the given name.
*
* @param name The name to query.
* @return The connection which owns the name.
*/
public String GetNameOwner(String name);
/**
* Get the Unix UID that owns a connection name.
*
* @param connection_name The connection name.
* @return The Unix UID that owns it.
*/
public UInt32 GetConnectionUnixUser(String connection_name);
/**
* Start a service. If the given service is not provided
* by any application, it will be started according to the .service file
* for that service.
*
* @param name The service name to start.
* @param flags Unused.
* @return DBUS_START_REPLY constants.
*/
public UInt32 StartServiceByName(String name, UInt32 flags);
/**
* Request a name on the bus.
*
* @param name The name to request.
* @param flags DBUS_NAME flags.
* @return DBUS_REQUEST_NAME_REPLY constants.
*/
public UInt32 RequestName(String name, UInt32 flags);
/**
* Release a name on the bus.
*
* @param name The name to release.
* @return DBUS_RELEASE_NAME_REPLY constants.
*/
public UInt32 ReleaseName(String name);
/**
* Add a match rule.
* Will cause you to receive messages that aren't directed to you which
* match this rule.
*
* @param matchrule The Match rule as a string. Format Undocumented.
*/
public void AddMatch(String matchrule) throws Error.MatchRuleInvalid;
/**
* Remove a match rule.
* Will cause you to stop receiving messages that aren't directed to you which
* match this rule.
*
* @param matchrule The Match rule as a string. Format Undocumented.
*/
public void RemoveMatch(String matchrule) throws Error.MatchRuleInvalid;
/**
* List the connections currently queued for a name.
*
* @param name The name to query
* @return A list of unique connection IDs.
*/
public String[] ListQueuedOwners(String name);
/**
* Returns the proccess ID associated with a connection.
*
* @param connection_name The name of the connection
* @return The PID of the connection.
*/
public UInt32 GetConnectionUnixProcessID(String connection_name);
/**
* Does something undocumented.
*/
public Byte[] GetConnectionSELinuxSecurityContext(String a);
/**
* Does something undocumented.
*/
public void ReloadConfig();
/**
* Signal sent when the owner of a name changes
*/
public class NameOwnerChanged extends DBusSignal {
public final String name;
public final String old_owner;
public final String new_owner;
public NameOwnerChanged(String path, String name, String old_owner, String new_owner) throws DBusException {
super(path, new Object[]{name, old_owner, new_owner});
this.name = name;
this.old_owner = old_owner;
this.new_owner = new_owner;
}
}
/**
* Signal sent to a connection when it loses a name
*/
public class NameLost extends DBusSignal {
public final String name;
public NameLost(String path, String name) throws DBusException {
super(path, name);
this.name = name;
}
}
/**
* Signal sent to a connection when it aquires a name
*/
public class NameAcquired extends DBusSignal {
public final String name;
public NameAcquired(String path, String name) throws DBusException {
super(path, name);
this.name = name;
}
}
/**
* Contains standard errors that can be thrown from methods.
*/
public interface Error {
/**
* Thrown if the method called was unknown on the remote object
*/
@SuppressWarnings("serial")
public class UnknownMethod extends DBusExecutionException {
public UnknownMethod(String message) {
super(message);
}
}
/**
* Thrown if the object was unknown on a remote connection
*/
@SuppressWarnings("serial")
public class UnknownObject extends DBusExecutionException {
public UnknownObject(String message) {
super(message);
}
}
/**
* Thrown if the requested service was not available
*/
@SuppressWarnings("serial")
public class ServiceUnknown extends DBusExecutionException {
public ServiceUnknown(String message) {
super(message);
}
}
/**
* Thrown if the match rule is invalid
*/
@SuppressWarnings("serial")
public class MatchRuleInvalid extends DBusExecutionException {
public MatchRuleInvalid(String message) {
super(message);
}
}
/**
* Thrown if there is no reply to a method call
*/
@SuppressWarnings("serial")
public class NoReply extends DBusExecutionException {
public NoReply(String message) {
super(message);
}
}
/**
* Thrown if a message is denied due to a security policy
*/
@SuppressWarnings("serial")
public class AccessDenied extends DBusExecutionException {
public AccessDenied(String message) {
super(message);
}
}
}
/**
* Description of the interface or method, returned in the introspection data
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
String value();
}
/**
* Indicates that a DBus interface or method is deprecated
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}
/**
* Contains method-specific annotations
*/
public interface Method {
/**
* Methods annotated with this do not send a reply
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoReply {
}
/**
* Give an error that the method can return
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Error {
String value();
}
}
/**
* Contains GLib-specific annotations
*/
public interface GLib {
/**
* Define a C symbol to map to this method. Used by GLib only
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CSymbol {
String value();
}
}
/**
* Contains Binding-test interfaces
*/
public interface Binding {
public interface SingleTests extends DBusInterface {
@Description("Returns the sum of the values in the input list")
public UInt32 Sum(byte[] a);
}
public interface TestClient extends DBusInterface {
@Description("when the trigger signal is received, this method should be called on the sending process/object.")
public void Response(UInt16 a, double b);
@Description("Causes a callback")
public static class Trigger extends DBusSignal {
public final UInt16 a;
public final double b;
public Trigger(String path, UInt16 a, double b) throws DBusException {
super(path, a, b);
this.a = a;
this.b = b;
}
}
}
public interface Tests extends DBusInterface {
@Description("Returns whatever it is passed")
public <T> Variant<T> Identity(Variant<T> input);
@Description("Returns whatever it is passed")
public byte IdentityByte(byte input);
@Description("Returns whatever it is passed")
public boolean IdentityBool(boolean input);
@Description("Returns whatever it is passed")
public short IdentityInt16(short input);
@Description("Returns whatever it is passed")
public UInt16 IdentityUInt16(UInt16 input);
@Description("Returns whatever it is passed")
public int IdentityInt32(int input);
@Description("Returns whatever it is passed")
public UInt32 IdentityUInt32(UInt32 input);
@Description("Returns whatever it is passed")
public long IdentityInt64(long input);
@Description("Returns whatever it is passed")
public UInt64 IdentityUInt64(UInt64 input);
@Description("Returns whatever it is passed")
public double IdentityDouble(double input);
@Description("Returns whatever it is passed")
public String IdentityString(String input);
@Description("Returns whatever it is passed")
public <T> Variant<T>[] IdentityArray(Variant<T>[] input);
@Description("Returns whatever it is passed")
public byte[] IdentityByteArray(byte[] input);
@Description("Returns whatever it is passed")
public boolean[] IdentityBoolArray(boolean[] input);
@Description("Returns whatever it is passed")
public short[] IdentityInt16Array(short[] input);
@Description("Returns whatever it is passed")
public UInt16[] IdentityUInt16Array(UInt16[] input);
@Description("Returns whatever it is passed")
public int[] IdentityInt32Array(int[] input);
@Description("Returns whatever it is passed")
public UInt32[] IdentityUInt32Array(UInt32[] input);
@Description("Returns whatever it is passed")
public long[] IdentityInt64Array(long[] input);
@Description("Returns whatever it is passed")
public UInt64[] IdentityUInt64Array(UInt64[] input);
@Description("Returns whatever it is passed")
public double[] IdentityDoubleArray(double[] input);
@Description("Returns whatever it is passed")
public String[] IdentityStringArray(String[] input);
@Description("Returns the sum of the values in the input list")
public long Sum(int[] a);
@Description("Given a map of A => B, should return a map of B => a list of all the As which mapped to B")
public Map<String, List<String>> InvertMapping(Map<String, String> a);
@Description("This method returns the contents of a struct as separate values")
public Triplet<String, UInt32, Short> DeStruct(TestStruct a);
@Description("Given any compound type as a variant, return all the primitive types recursively contained within as an array of variants")
public List<Variant<Object>> Primitize(Variant<Object> a);
@Description("inverts it's input")
public boolean Invert(boolean a);
@Description("triggers sending of a signal from the supplied object with the given parameter")
public void Trigger(String a, UInt64 b);
@Description("Causes the server to exit")
public void Exit();
}
public interface TestSignals extends DBusInterface {
@Description("Sent in response to a method call")
public static class Triggered extends DBusSignal {
public final UInt64 a;
public Triggered(String path, UInt64 a) throws DBusException {
super(path, a);
this.a = a;
}
}
}
public final class Triplet<A, B, C> extends Tuple {
@Position(0)
public final A a;
@Position(1)
public final B b;
@Position(2)
public final C c;
public Triplet(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
}
public final class TestStruct extends Struct {
@Position(0)
public final String a;
@Position(1)
public final UInt32 b;
@Position(2)
public final Short c;
public TestStruct(String a, UInt32 b, Short c) {
this.a = a;
this.b = b;
this.c = c;
}
}
}
}

View file

@ -1,173 +1,180 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug; import org.slf4j.LoggerFactory;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Hashtable; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.freedesktop.dbus.Gettext.getString; public final class ArrayFrob {
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER = new ConcurrentHashMap<>();
class ArrayFrob { private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE = new ConcurrentHashMap<>();
static Hashtable<Class<? extends Object>, Class<? extends Object>> primitiveToWrapper = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
static Hashtable<Class<? extends Object>, Class<? extends Object>> wrapperToPrimitive = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
static { static {
primitiveToWrapper.put(Boolean.TYPE, Boolean.class); PRIMITIVE_TO_WRAPPER.put(Boolean.TYPE, Boolean.class);
primitiveToWrapper.put(Byte.TYPE, Byte.class); PRIMITIVE_TO_WRAPPER.put(Byte.TYPE, Byte.class);
primitiveToWrapper.put(Short.TYPE, Short.class); PRIMITIVE_TO_WRAPPER.put(Short.TYPE, Short.class);
primitiveToWrapper.put(Character.TYPE, Character.class); PRIMITIVE_TO_WRAPPER.put(Character.TYPE, Character.class);
primitiveToWrapper.put(Integer.TYPE, Integer.class); PRIMITIVE_TO_WRAPPER.put(Integer.TYPE, Integer.class);
primitiveToWrapper.put(Long.TYPE, Long.class); PRIMITIVE_TO_WRAPPER.put(Long.TYPE, Long.class);
primitiveToWrapper.put(Float.TYPE, Float.class); PRIMITIVE_TO_WRAPPER.put(Float.TYPE, Float.class);
primitiveToWrapper.put(Double.TYPE, Double.class); PRIMITIVE_TO_WRAPPER.put(Double.TYPE, Double.class);
wrapperToPrimitive.put(Boolean.class, Boolean.TYPE); WRAPPER_TO_PRIMITIVE.put(Boolean.class, Boolean.TYPE);
wrapperToPrimitive.put(Byte.class, Byte.TYPE); WRAPPER_TO_PRIMITIVE.put(Byte.class, Byte.TYPE);
wrapperToPrimitive.put(Short.class, Short.TYPE); WRAPPER_TO_PRIMITIVE.put(Short.class, Short.TYPE);
wrapperToPrimitive.put(Character.class, Character.TYPE); WRAPPER_TO_PRIMITIVE.put(Character.class, Character.TYPE);
wrapperToPrimitive.put(Integer.class, Integer.TYPE); WRAPPER_TO_PRIMITIVE.put(Integer.class, Integer.TYPE);
wrapperToPrimitive.put(Long.class, Long.TYPE); WRAPPER_TO_PRIMITIVE.put(Long.class, Long.TYPE);
wrapperToPrimitive.put(Float.class, Float.TYPE); WRAPPER_TO_PRIMITIVE.put(Float.class, Float.TYPE);
wrapperToPrimitive.put(Double.class, Double.TYPE); WRAPPER_TO_PRIMITIVE.put(Double.class, Double.TYPE);
}
private ArrayFrob() {
}
public static Map<Class<?>, Class<?>> getPrimitiveToWrapperTypes() {
return Collections.unmodifiableMap(PRIMITIVE_TO_WRAPPER);
}
public static Map<Class<?>, Class<?>> getWrapperToPrimitiveTypes() {
return Collections.unmodifiableMap(WRAPPER_TO_PRIMITIVE);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T[] wrap(Object o) throws IllegalArgumentException { public static <T> T[] wrap(Object _o) throws IllegalArgumentException {
Class<? extends Object> ac = o.getClass(); Class<? extends Object> ac = _o.getClass();
if (!ac.isArray()) throw new IllegalArgumentException(getString("invalidArray")); if (!ac.isArray()) {
throw new IllegalArgumentException("Not an array");
}
Class<? extends Object> cc = ac.getComponentType(); Class<? extends Object> cc = ac.getComponentType();
Class<? extends Object> ncc = primitiveToWrapper.get(cc); Class<? extends Object> ncc = PRIMITIVE_TO_WRAPPER.get(cc);
if (null == ncc) throw new IllegalArgumentException(getString("notPrimitiveType")); if (null == ncc) {
T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o)); throw new IllegalArgumentException("Not a primitive type");
for (int i = 0; i < ns.length; i++) }
ns[i] = (T) Array.get(o, i); T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(_o));
for (int i = 0; i < ns.length; i++) {
ns[i] = (T) Array.get(_o, i);
}
return ns; return ns;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Object unwrap(T[] ns) throws IllegalArgumentException { public static <T> Object unwrap(T[] _ns) throws IllegalArgumentException {
Class<? extends T[]> ac = (Class<? extends T[]>) ns.getClass(); Class<? extends T[]> ac = (Class<? extends T[]>) _ns.getClass();
Class<T> cc = (Class<T>) ac.getComponentType(); Class<T> cc = (Class<T>) ac.getComponentType();
Class<? extends Object> ncc = wrapperToPrimitive.get(cc); Class<? extends Object> ncc = WRAPPER_TO_PRIMITIVE.get(cc);
if (null == ncc) throw new IllegalArgumentException(getString("invalidWrapperType")); if (null == ncc) {
Object o = Array.newInstance(ncc, ns.length); throw new IllegalArgumentException("Not a wrapper type");
for (int i = 0; i < ns.length; i++) }
Array.set(o, i, ns[i]); Object o = Array.newInstance(ncc, _ns.length);
for (int i = 0; i < _ns.length; i++) {
Array.set(o, i, _ns[i]);
}
return o; return o;
} }
public static <T> List<T> listify(T[] ns) throws IllegalArgumentException { public static <T> List<T> listify(T[] _ns) throws IllegalArgumentException {
return Arrays.asList(ns); return Arrays.asList(_ns);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> List<T> listify(Object o) throws IllegalArgumentException { public static <T> List<T> listify(Object _o) throws IllegalArgumentException {
if (o instanceof Object[]) return listify((T[]) o); if (_o instanceof Object[]) {
if (!o.getClass().isArray()) throw new IllegalArgumentException(getString("invalidArray")); return listify((T[]) _o);
List<T> l = new ArrayList<T>(Array.getLength(o)); }
for (int i = 0; i < Array.getLength(o); i++) if (!_o.getClass().isArray()) {
l.add((T) Array.get(o, i)); throw new IllegalArgumentException("Not an array");
}
List<T> l = new ArrayList<>(Array.getLength(_o));
for (int i = 0; i < Array.getLength(_o); i++) {
l.add((T) Array.get(_o, i));
}
return l; return l;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T[] delist(List<T> l, Class<T> c) throws IllegalArgumentException { public static <T> T[] delist(List<T> _l, Class<T> _c) throws IllegalArgumentException {
return l.toArray((T[]) Array.newInstance(c, 0)); return _l.toArray((T[]) Array.newInstance(_c, 0));
} }
public static <T> Object delistprimitive(List<T> l, Class<T> c) throws IllegalArgumentException { public static <T> Object delistprimitive(List<T> _l, Class<T> _c) throws IllegalArgumentException {
Object o = Array.newInstance(c, l.size()); Object o = Array.newInstance(_c, _l.size());
for (int i = 0; i < l.size(); i++) for (int i = 0; i < _l.size(); i++) {
Array.set(o, i, l.get(i)); Array.set(o, i, _l.get(i));
}
return o; return o;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Object convert(Object o, Class<? extends Object> c) throws IllegalArgumentException { public static Object convert(Object _o, Class<? extends Object> _c) throws IllegalArgumentException {
/* Possible Conversions: /* Possible Conversions:
* *
** List<Integer> -> List<Integer> ** List<Integer> -> List<Integer>
** List<Integer> -> int[] ** List<Integer> -> int[]
** List<Integer> -> Integer[] ** List<Integer> -> Integer[]
** int[] -> int[] ** int[] -> int[]
** int[] -> List<Integer> ** int[] -> List<Integer>
** int[] -> Integer[] ** int[] -> Integer[]
** Integer[] -> Integer[] ** Integer[] -> Integer[]
** Integer[] -> int[] ** Integer[] -> int[]
** Integer[] -> List<Integer> ** Integer[] -> List<Integer>
*/ */
try { try {
// List<Integer> -> List<Integer> // List<Integer> -> List<Integer>
if (List.class.equals(c) if (List.class.equals(_c) && _o instanceof List) {
&& o instanceof List) return _o;
return o; }
// int[] -> List<Integer> // int[] -> List<Integer>
// Integer[] -> List<Integer> // Integer[] -> List<Integer>
if (List.class.equals(c) if (List.class.equals(_c) && _o.getClass().isArray()) {
&& o.getClass().isArray()) return listify(_o);
return listify(o); }
// int[] -> int[] // int[] -> int[]
// Integer[] -> Integer[] // Integer[] -> Integer[]
if (o.getClass().isArray() if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().equals(_c.getComponentType())) {
&& c.isArray() return _o;
&& o.getClass().getComponentType().equals(c.getComponentType())) }
return o;
// int[] -> Integer[] // int[] -> Integer[]
if (o.getClass().isArray() if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().isPrimitive()) {
&& c.isArray() return wrap(_o);
&& o.getClass().getComponentType().isPrimitive()) }
return wrap(o);
// Integer[] -> int[] // Integer[] -> int[]
if (o.getClass().isArray() if (_o.getClass().isArray() && _c.isArray() && _c.getComponentType().isPrimitive()) {
&& c.isArray() return unwrap((Object[]) _o);
&& c.getComponentType().isPrimitive()) }
return unwrap((Object[]) o);
// List<Integer> -> int[] // List<Integer> -> int[]
if (o instanceof List if (_o instanceof List && _c.isArray() && _c.getComponentType().isPrimitive()) {
&& c.isArray() return delistprimitive((List<Object>) _o, (Class<Object>) _c.getComponentType());
&& c.getComponentType().isPrimitive()) }
return delistprimitive((List<Object>) o, (Class<Object>) c.getComponentType());
// List<Integer> -> Integer[] // List<Integer> -> Integer[]
if (o instanceof List if (_o instanceof List && _c.isArray()) {
&& c.isArray()) return delist((List<Object>) _o, (Class<Object>) _c.getComponentType());
return delist((List<Object>) o, (Class<Object>) c.getComponentType()); }
if (o.getClass().isArray() if (_o.getClass().isArray() && _c.isArray()) {
&& c.isArray()) return type((Object[]) _o, (Class<Object>) _c.getComponentType());
return type((Object[]) o, (Class<Object>) c.getComponentType()); }
} catch (Exception e) { } catch (Exception _ex) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); LoggerFactory.getLogger(ArrayFrob.class).debug("Cannot convert object.", _ex);
throw new IllegalArgumentException(e); throw new IllegalArgumentException(_ex);
} }
throw new IllegalArgumentException(MessageFormat.format(getString("convertionTypeNotExpected"), new Object[]{o.getClass(), c})); throw new IllegalArgumentException(String.format("Not An Expected Convertion type from %s to %s", _o.getClass(), _c));
} }
public static Object[] type(Object[] old, Class<Object> c) { public static Object[] type(Object[] _old, Class<Object> _c) {
Object[] ns = (Object[]) Array.newInstance(c, old.length); Object[] ns = (Object[]) Array.newInstance(_c, _old.length);
for (int i = 0; i < ns.length; i++) System.arraycopy(_old, 0, ns, 0, ns.length);
ns[i] = old[i];
return ns; return ns;
} }
} }

View file

@ -1,52 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import static org.freedesktop.dbus.Gettext.getString;
public class BusAddress {
private String type;
private Map<String, String> parameters;
public BusAddress(String address) throws ParseException {
if (null == address || "".equals(address)) throw new ParseException(getString("busAddressBlank"), 0);
if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing bus address: " + address);
String[] ss = address.split(":", 2);
if (ss.length < 2) throw new ParseException(getString("busAddressInvalid") + address, 0);
type = ss[0];
if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport type: " + type);
String[] ps = ss[1].split(",");
parameters = new HashMap<String, String>();
for (String p : ps) {
String[] kv = p.split("=", 2);
parameters.put(kv[0], kv[1]);
}
if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport options: " + parameters);
}
public String getType() {
return type;
}
public String getParameter(String key) {
return parameters.get(key);
}
public String toString() {
return type + ": " + parameters;
}
}

View file

@ -1,22 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
/**
* Interface for callbacks in async mode
*/
public interface CallbackHandler<ReturnType> {
public void handle(ReturnType r);
public void handleError(DBusExecutionException e);
}

View file

@ -1,39 +1,25 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import org.freedesktop.dbus.annotations.Position;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/** /**
* This class is the super class of both Structs and Tuples * This class is the super class of both Structs and Tuples
* and holds common methods. * and holds common methods.
*/ */
abstract class Container { public abstract class Container {
private static Map<Type, Type[]> typecache = new HashMap<Type, Type[]>(); private static final Map<Type, Type[]> TYPE_CACHE = new HashMap<>();
private Object[] parameters = null;
static void putTypeCache(Type k, Type[] v) { Container() {
typecache.put(k, v);
}
static Type[] getTypeCache(Type k) {
return typecache.get(k);
}
private Object[] parameters = null;
public Container() {
} }
private void setup() { private void setup() {
@ -43,13 +29,16 @@ abstract class Container {
int diff = 0; int diff = 0;
for (Field f : fs) { for (Field f : fs) {
Position p = f.getAnnotation(Position.class); Position p = f.getAnnotation(Position.class);
f.setAccessible(true);
if (null == p) { if (null == p) {
diff++; diff++;
continue; continue;
} }
try { try {
args[p.value()] = f.get(this); args[p.value()] = f.get(this);
} catch (IllegalAccessException IAe) { } catch (IllegalAccessException _exIa) {
LoggerFactory.getLogger(getClass()).trace("Could not set value", _exIa);
} }
} }
@ -58,36 +47,67 @@ abstract class Container {
} }
/** /**
* Returns the struct contents in order. * Returns the struct contents in order.
* * @return object array
* @throws DBusException If there is a problem doing this. */
*/
public final Object[] getParameters() { public final Object[] getParameters() {
if (null != parameters) return parameters; if (null != parameters) {
return parameters;
}
setup(); setup();
return parameters; return parameters;
} }
/** /** Returns this struct as a string. */
* Returns this struct as a string. @Override
*/
public final String toString() { public final String toString() {
String s = getClass().getName() + "<"; StringBuilder sb = new StringBuilder();
if (null == parameters) sb.append(getClass().getName()).append("<");
if (null == parameters) {
setup(); setup();
if (0 == parameters.length) }
return s + ">"; if (0 == parameters.length) {
for (Object o : parameters) return sb.append(">").toString();
s += o + ", "; }
return s.replaceAll(", $", ">"); sb.append(Arrays.stream(parameters).map(o -> Objects.toString(o)).collect(Collectors.joining(", ")));
return sb.append(">").toString();
} }
public final boolean equals(Object other) { @Override
if (other instanceof Container) { public final boolean equals(Object _other) {
Container that = (Container) other; if (this == _other) {
if (this.getClass().equals(that.getClass())) return true;
}
if (_other == null) {
return false;
}
if (_other instanceof Container) {
Container that = (Container) _other;
if (this.getClass().equals(that.getClass())) {
return Arrays.equals(this.getParameters(), that.getParameters()); return Arrays.equals(this.getParameters(), that.getParameters());
else return false; } else {
} else return false; return false;
}
} else {
return false;
}
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.deepHashCode(parameters);
return result;
}
static void putTypeCache(Type _k, Type[] _v) {
TYPE_CACHE.put(_k, _v);
}
static Type[] getTypeCache(Type _k) {
return TYPE_CACHE.get(_k);
}
} }

View file

@ -1,117 +1,122 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug; import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.DBus.Error.NoReply; import org.freedesktop.dbus.errors.Error;
import org.freedesktop.dbus.errors.NoReply;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.messages.MethodReturn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import static org.freedesktop.dbus.Gettext.getString;
/** /**
* A handle to an asynchronous method call. * A handle to an asynchronous method call.
*/ */
public class DBusAsyncReply<ReturnType> { public class DBusAsyncReply<T> {
/**
* Check if any of a set of asynchronous calls have had a reply.
*
* @param replies A Collection of handles to replies to check.
* @return A Collection only containing those calls which have had replies.
*/
public static Collection<DBusAsyncReply<? extends Object>> hasReply(Collection<DBusAsyncReply<? extends Object>> replies) {
Collection<DBusAsyncReply<? extends Object>> c = new ArrayList<DBusAsyncReply<? extends Object>>(replies);
Iterator<DBusAsyncReply<? extends Object>> i = c.iterator();
while (i.hasNext())
if (!i.next().hasReply()) i.remove();
return c;
}
private ReturnType rval = null; private final Logger logger = LoggerFactory.getLogger(getClass());
private DBusExecutionException error = null;
private MethodCall mc;
private Method me;
private AbstractConnection conn;
DBusAsyncReply(MethodCall mc, Method me, AbstractConnection conn) { private T rval = null;
this.mc = mc; private DBusExecutionException error = null;
this.me = me; private final MethodCall mc;
this.conn = conn; private final Method me;
private final AbstractConnection conn;
public DBusAsyncReply(MethodCall _mc, Method _me, AbstractConnection _conn) {
this.mc = _mc;
this.me = _me;
this.conn = _conn;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private synchronized void checkReply() { private synchronized void checkReply() {
if (mc.hasReply()) { if (mc.hasReply()) {
Message m = mc.getReply(); Message m = mc.getReply();
if (m instanceof Error) if (m instanceof Error) {
error = ((Error) m).getException(); error = ((Error) m).getException();
else if (m instanceof MethodReturn) { } else if (m instanceof MethodReturn) {
try { try {
rval = (ReturnType) RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn); Object obj = RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn);
} catch (DBusExecutionException DBEe) {
error = DBEe; rval = (T) obj;
} catch (DBusException DBe) { } catch (DBusExecutionException _ex) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); error = _ex;
error = new DBusExecutionException(DBe.getMessage()); } catch (DBusException _ex) {
logger.debug("", _ex);
error = new DBusExecutionException(_ex.getMessage());
} }
} }
} }
} }
/** /**
* Check if we've had a reply. * Check if we've had a reply.
* * @return true if we have a reply
* @return True if we have a reply */
*/
public boolean hasReply() { public boolean hasReply() {
if (null != rval || null != error) return true; if (null != rval || null != error) {
return true;
}
checkReply(); checkReply();
return null != rval || null != error; return null != rval || null != error;
} }
/** /**
* Get the reply. * Get the reply.
* * @return The return value from the method.
* @return The return value from the method. * @throws DBusException if the reply to the method was an error.
* @throws DBusExecutionException if the reply to the method was an error. * @throws NoReply if the method hasn't had a reply yet
* @throws NoReply if the method hasn't had a reply yet */
*/ public T getReply() throws DBusException {
public ReturnType getReply() throws DBusExecutionException { if (null != rval) {
if (null != rval) return rval; return rval;
else if (null != error) throw error; } else if (null != error) {
throw error;
}
checkReply(); checkReply();
if (null != rval) return rval; if (null != rval) {
else if (null != error) throw error; return rval;
else throw new NoReply(getString("asyncCallNoReply")); } else if (null != error) {
throw error;
} else {
throw new NoReply("Async call has not had a reply");
}
} }
@Override
public String toString() { public String toString() {
return getString("waitingFor") + mc; return "Waiting for: " + mc;
} }
Method getMethod() { public Method getMethod() {
return me; return me;
} }
AbstractConnection getConnection() { public AbstractConnection getConnection() {
return conn; return conn;
} }
MethodCall getCall() { public MethodCall getCall() {
return mc; return mc;
} }
}
/**
* Check if any of a set of asynchronous calls have had a reply.
* @param _replies A Collection of handles to replies to check.
* @return A Collection only containing those calls which have had replies.
*/
// public static Collection<DBusAsyncReply<?>> hasReply(Collection<DBusAsyncReply<?>> _replies) {
// Collection<DBusAsyncReply<?>> c = new ArrayList<>(_replies);
// Iterator<DBusAsyncReply<?>> i = c.iterator();
// while (i.hasNext()) {
// if (!i.next().hasReply()) {
// i.remove();
// }
// }
// return c;
// }
}

View file

@ -1,77 +1,69 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import org.freedesktop.dbus.messages.Message;
/** /**
* Holds information on a method call * Holds information on a method call
*/ */
public class DBusCallInfo { public class DBusCallInfo {
/** /**
* Indicates the caller won't wait for a reply (and we won't send one). * Indicates the caller won't wait for a reply (and we won't send one).
*/ */
public static final int NO_REPLY = Message.Flags.NO_REPLY_EXPECTED; public static final int NO_REPLY = Message.Flags.NO_REPLY_EXPECTED;
public static final int ASYNC = 0x100; public static final int ASYNC = 0x100;
private String source; private final String source;
private String destination; private final String destination;
private String objectpath; private final String objectpath;
private String iface; private final String iface;
private String method; private final String method;
private int flags; private final int flags;
DBusCallInfo(Message m) { public DBusCallInfo(Message _m) {
this.source = m.getSource(); source = _m.getSource();
this.destination = m.getDestination(); destination = _m.getDestination();
this.objectpath = m.getPath(); objectpath = _m.getPath();
this.iface = m.getInterface(); iface = _m.getInterface();
this.method = m.getName(); method = _m.getName();
this.flags = m.getFlags(); flags = _m.getFlags();
} }
/** /** Returns the BusID which called the method.
* Returns the BusID which called the method * @return source
*/ */
public String getSource() { public String getSource() {
return source; return source;
} }
/** /** Returns the name with which we were addressed on the Bus.
* Returns the name with which we were addressed on the Bus * @return destination
*/ */
public String getDestination() { public String getDestination() {
return destination; return destination;
} }
/** /** Returns the object path used to call this method.
* Returns the object path used to call this method * @return objectpath
*/ */
public String getObjectPath() { public String getObjectPath() {
return objectpath; return objectpath;
} }
/** /** Returns the interface this method was called with.
* Returns the interface this method was called with * @return interface
*/ */
public String getInterface() { public String getInterface() {
return iface; return iface;
} }
/** /** Returns the method name used to call this method.
* Returns the method name used to call this method * @return method
*/ */
public String getMethod() { public String getMethod() {
return method; return method;
} }
/** /** Returns any flags set on this method call.
* Returns any flags set on this method call * @return flags
*/ */
public int getFlags() { public int getFlags() {
return flags; return flags;

View file

@ -1,794 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import org.freedesktop.DBus;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.NotConnected;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
/**
* Handles a connection to DBus.
* <p>
* This is a Singleton class, only 1 connection to the SYSTEM or SESSION busses can be made.
* Repeated calls to getConnection will return the same reference.
* </p>
* <p>
* Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues.
* </p>
*/
public class DBusConnection extends AbstractConnection {
/**
* Add addresses of peers to a set which will watch for them to
* disappear and automatically remove them from the set.
*/
public class PeerSet implements Set<String>, DBusSigHandler<DBus.NameOwnerChanged> {
private Set<String> addresses;
public PeerSet() {
addresses = new TreeSet<String>();
try {
addSigHandler(new DBusMatchRule(DBus.NameOwnerChanged.class, null, null), this);
} catch (DBusException DBe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
}
}
public void handle(DBus.NameOwnerChanged noc) {
if (Debug.debug)
Debug.print(Debug.DEBUG, "Received NameOwnerChanged(" + noc.name + "," + noc.old_owner + "," + noc.new_owner + ")");
if ("".equals(noc.new_owner) && addresses.contains(noc.name))
remove(noc.name);
}
public boolean add(String address) {
if (Debug.debug)
Debug.print(Debug.DEBUG, "Adding " + address);
synchronized (addresses) {
return addresses.add(address);
}
}
public boolean addAll(Collection<? extends String> addresses) {
synchronized (this.addresses) {
return this.addresses.addAll(addresses);
}
}
public void clear() {
synchronized (addresses) {
addresses.clear();
}
}
public boolean contains(Object o) {
return addresses.contains(o);
}
public boolean containsAll(Collection<?> os) {
return addresses.containsAll(os);
}
public boolean equals(Object o) {
if (o instanceof PeerSet)
return ((PeerSet) o).addresses.equals(addresses);
else return false;
}
public int hashCode() {
return addresses.hashCode();
}
public boolean isEmpty() {
return addresses.isEmpty();
}
public Iterator<String> iterator() {
return addresses.iterator();
}
public boolean remove(Object o) {
if (Debug.debug)
Debug.print(Debug.DEBUG, "Removing " + o);
synchronized (addresses) {
return addresses.remove(o);
}
}
public boolean removeAll(Collection<?> os) {
synchronized (addresses) {
return addresses.removeAll(os);
}
}
public boolean retainAll(Collection<?> os) {
synchronized (addresses) {
return addresses.retainAll(os);
}
}
public int size() {
return addresses.size();
}
public Object[] toArray() {
synchronized (addresses) {
return addresses.toArray();
}
}
public <T> T[] toArray(T[] a) {
synchronized (addresses) {
return addresses.toArray(a);
}
}
}
private class _sighandler implements DBusSigHandler<DBusSignal> {
public void handle(DBusSignal s) {
if (s instanceof org.freedesktop.DBus.Local.Disconnected) {
if (Debug.debug) Debug.print(Debug.WARN, "Handling disconnected signal from bus");
try {
Error err = new Error(
"org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")});
if (null != pendingCalls) synchronized (pendingCalls) {
long[] set = pendingCalls.getKeys();
for (long l : set)
if (-1 != l) {
MethodCall m = pendingCalls.remove(l);
if (null != m)
m.setReply(err);
}
}
synchronized (pendingErrors) {
pendingErrors.add(err);
}
} catch (DBusException DBe) {
}
} else if (s instanceof org.freedesktop.DBus.NameAcquired) {
busnames.add(((org.freedesktop.DBus.NameAcquired) s).name);
}
}
}
/**
* System Bus
*/
public static final int SYSTEM = 0;
/**
* Session Bus
*/
public static final int SESSION = 1;
public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket";
private List<String> busnames;
private static final Map<Object, DBusConnection> conn = new HashMap<Object, DBusConnection>();
private int _refcount = 0;
private Object _reflock = new Object();
private DBus _dbus;
/**
* Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned.
*
* @param address The address of the bus to connect to
* @throws DBusException If there is a problem connecting to the Bus.
*/
public static DBusConnection getConnection(String address) throws DBusException {
synchronized (conn) {
DBusConnection c = conn.get(address);
if (null != c) {
synchronized (c._reflock) {
c._refcount++;
}
return c;
} else {
c = new DBusConnection(address);
conn.put(address, c);
return c;
}
}
}
/**
* Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned.
*
* @param bustype The Bus to connect to.
* @throws DBusException If there is a problem connecting to the Bus.
* @see #SYSTEM
* @see #SESSION
*/
public static DBusConnection getConnection(int bustype) throws DBusException {
synchronized (conn) {
String s = null;
switch (bustype) {
case SYSTEM:
s = System.getenv("DBUS_SYSTEM_BUS_ADDRESS");
if (null == s) s = DEFAULT_SYSTEM_BUS_ADDRESS;
break;
case SESSION:
s = System.getenv("DBUS_SESSION_BUS_ADDRESS");
if (null == s) {
// address gets stashed in $HOME/.dbus/session-bus/`dbus-uuidgen --get`-`sed 's/:\(.\)\..*/\1/' <<< $DISPLAY`
String display = System.getenv("DISPLAY");
if (null == display) throw new DBusException(getString("cannotResolveSessionBusAddress"));
File uuidfile = new File("/var/lib/dbus/machine-id");
if (!uuidfile.exists()) throw new DBusException(getString("cannotResolveSessionBusAddress"));
try (BufferedReader r = new BufferedReader(new FileReader(uuidfile))) {
String uuid = r.readLine();
String homedir = System.getProperty("user.home");
File addressfile = new File(homedir + "/.dbus/session-bus",
uuid + "-" + display.replaceAll(":([0-9]*)\\..*", "$1"));
if (!addressfile.exists())
throw new DBusException(getString("cannotResolveSessionBusAddress"));
try (BufferedReader r2 = new BufferedReader(new FileReader(addressfile))) {
String l;
while (null != (l = r2.readLine())) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Reading D-Bus session data: " + l);
if (l.matches("DBUS_SESSION_BUS_ADDRESS.*")) {
s = l.replaceAll("^[^=]*=", "");
if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing " + l + " to " + s);
}
}
}
if (null == s || "".equals(s))
throw new DBusException(getString("cannotResolveSessionBusAddress"));
if (Debug.debug)
Debug.print(Debug.INFO, "Read bus address " + s + " from file " + addressfile.toString());
} catch (Exception e) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
throw new DBusException(getString("cannotResolveSessionBusAddress"));
}
}
break;
default:
throw new DBusException(getString("invalidBusType") + bustype);
}
DBusConnection c = conn.get(s);
if (Debug.debug) Debug.print(Debug.VERBOSE, "Getting bus connection for " + s + ": " + c);
if (null != c) {
synchronized (c._reflock) {
c._refcount++;
}
return c;
} else {
if (Debug.debug) Debug.print(Debug.DEBUG, "Creating new bus connection to: " + s);
c = new DBusConnection(s);
conn.put(s, c);
return c;
}
}
}
@SuppressWarnings("unchecked")
private DBusConnection(String address) throws DBusException {
super(address);
busnames = new Vector<String>();
synchronized (_reflock) {
_refcount = 1;
}
try {
transport = new Transport(addr, AbstractConnection.TIMEOUT);
connected = true;
} catch (IOException IOe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe);
disconnect();
throw new DBusException(getString("connectionFailure") + IOe.getMessage());
} catch (ParseException Pe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Pe);
disconnect();
throw new DBusException(getString("connectionFailure") + Pe.getMessage());
}
// start listening for calls
listen();
// register disconnect handlers
DBusSigHandler h = new _sighandler();
addSigHandlerWithoutMatch(org.freedesktop.DBus.Local.Disconnected.class, h);
addSigHandlerWithoutMatch(org.freedesktop.DBus.NameAcquired.class, h);
// register ourselves
_dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class);
try {
busnames.add(_dbus.Hello());
} catch (DBusExecutionException DBEe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe);
throw new DBusException(DBEe.getMessage());
}
}
@SuppressWarnings("unchecked")
DBusInterface dynamicProxy(String source, String path) throws DBusException {
if (Debug.debug)
Debug.print(Debug.INFO, "Introspecting " + path + " on " + source + " for dynamic proxy creation");
try {
DBus.Introspectable intro = getRemoteObject(source, path, DBus.Introspectable.class);
String data = intro.Introspect();
if (Debug.debug) Debug.print(Debug.VERBOSE, "Got introspection data: " + data);
String[] tags = data.split("[<>]");
Vector<String> ifaces = new Vector<String>();
for (String tag : tags) {
if (tag.startsWith("interface")) {
ifaces.add(tag.replaceAll("^interface *name *= *['\"]([^'\"]*)['\"].*$", "$1"));
}
}
Vector<Class<? extends Object>> ifcs = new Vector<Class<? extends Object>>();
for (String iface : ifaces) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Trying interface " + iface);
int j = 0;
while (j >= 0) {
try {
Class ifclass = Class.forName(iface);
if (!ifcs.contains(ifclass))
ifcs.add(ifclass);
break;
} catch (Exception e) {
}
j = iface.lastIndexOf(".");
char[] cs = iface.toCharArray();
if (j >= 0) {
cs[j] = '$';
iface = String.valueOf(cs);
}
}
}
if (ifcs.size() == 0) throw new DBusException(getString("interfaceToCastNotFound"));
RemoteObject ro = new RemoteObject(source, path, null, false);
DBusInterface newi = (DBusInterface)
Proxy.newProxyInstance(ifcs.get(0).getClassLoader(),
ifcs.toArray(new Class[0]),
new RemoteInvocationHandler(this, ro));
importedObjects.put(newi, ro);
return newi;
} catch (Exception e) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
throw new DBusException(MessageFormat.format(getString("createProxyExportFailure"), new Object[]{path, source, e.getMessage()}));
}
}
DBusInterface getExportedObject(String source, String path) throws DBusException {
ExportedObject o = null;
synchronized (exportedObjects) {
o = exportedObjects.get(path);
}
if (null != o && null == o.object.get()) {
unExportObject(path);
o = null;
}
if (null != o) return o.object.get();
if (null == source) throw new DBusException(getString("objectNotExportedNoRemoteSpecified"));
return dynamicProxy(source, path);
}
/**
* Release a bus name.
* Releases the name so that other people can use it
*
* @param busname The name to release. MUST be in dot-notation like "org.freedesktop.local"
* @throws DBusException If the busname is incorrectly formatted.
*/
public void releaseBusName(String busname) throws DBusException {
if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName"));
synchronized (this.busnames) {
UInt32 rv;
try {
rv = _dbus.ReleaseName(busname);
} catch (DBusExecutionException DBEe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe);
throw new DBusException(DBEe.getMessage());
}
this.busnames.remove(busname);
}
}
/**
* Request a bus name.
* Request the well known name that this should respond to on the Bus.
*
* @param busname The name to respond to. MUST be in dot-notation like "org.freedesktop.local"
* @throws DBusException If the register name failed, or our name already exists on the bus.
* or if busname is incorrectly formatted.
*/
public void requestBusName(String busname) throws DBusException {
if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName"));
synchronized (this.busnames) {
UInt32 rv;
try {
rv = _dbus.RequestName(busname,
new UInt32(DBus.DBUS_NAME_FLAG_REPLACE_EXISTING |
DBus.DBUS_NAME_FLAG_DO_NOT_QUEUE));
} catch (DBusExecutionException DBEe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe);
throw new DBusException(DBEe.getMessage());
}
switch (rv.intValue()) {
case DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
break;
case DBus.DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
throw new DBusException(getString("dbusRegistrationFailure"));
case DBus.DBUS_REQUEST_NAME_REPLY_EXISTS:
throw new DBusException(getString("dbusRegistrationFailure"));
case DBus.DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
break;
default:
break;
}
this.busnames.add(busname);
}
}
/**
* Returns the unique name of this connection.
*/
public String getUniqueName() {
return busnames.get(0);
}
/**
* Returns all the names owned by this connection.
*/
public String[] getNames() {
Set<String> names = new TreeSet<String>();
names.addAll(busnames);
return names.toArray(new String[0]);
}
public <I extends DBusInterface> I getPeerRemoteObject(String busname, String objectpath, Class<I> type) throws DBusException {
return getPeerRemoteObject(busname, objectpath, type, true);
}
/**
* Return a reference to a remote object.
* This method will resolve the well known name (if given) to a unique bus name when you call it.
* This means that if a well known name is released by one process and acquired by another calls to
* objects gained from this method will continue to operate on the original process.
* <p>
* This method will use bus introspection to determine the interfaces on a remote object and so
* <b>may block</b> and <b>may fail</b>. The resulting proxy object will, however, be castable
* to any interface it implements. It will also autostart the process if applicable. Also note
* that the resulting proxy may fail to execute the correct method with overloaded methods
* and that complex types may fail in interesting ways. Basically, if something odd happens,
* try specifying the interface explicitly.
*
* @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local")
* or may be a DBus address such as ":1-16".
* @param objectpath The path on which the process is exporting the object.$
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted.
*/
public DBusInterface getPeerRemoteObject(String busname, String objectpath) throws DBusException {
if (null == busname) throw new DBusException(getString("nullBusName"));
if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX))
|| busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + busname);
String unique = _dbus.GetNameOwner(busname);
return dynamicProxy(unique, objectpath);
}
/**
* Return a reference to a remote object.
* This method will always refer to the well known name (if given) rather than resolving it to a unique bus name.
* In particular this means that if a process providing the well known name disappears and is taken over by another process
* proxy objects gained by this method will make calls on the new proccess.
* <p>
* This method will use bus introspection to determine the interfaces on a remote object and so
* <b>may block</b> and <b>may fail</b>. The resulting proxy object will, however, be castable
* to any interface it implements. It will also autostart the process if applicable. Also note
* that the resulting proxy may fail to execute the correct method with overloaded methods
* and that complex types may fail in interesting ways. Basically, if something odd happens,
* try specifying the interface explicitly.
*
* @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local")
* or may be a DBus address such as ":1-16".
* @param objectpath The path on which the process is exporting the object.
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted.
*/
public DBusInterface getRemoteObject(String busname, String objectpath) throws DBusException {
if (null == busname) throw new DBusException(getString("nullBusName"));
if (null == objectpath) throw new DBusException(getString("nullObjectPath"));
if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX))
|| busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + busname);
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + objectpath);
return dynamicProxy(busname, objectpath);
}
/**
* Return a reference to a remote object.
* This method will resolve the well known name (if given) to a unique bus name when you call it.
* This means that if a well known name is released by one process and acquired by another calls to
* objects gained from this method will continue to operate on the original process.
*
* @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local")
* or may be a DBus address such as ":1-16".
* @param objectpath The path on which the process is exporting the object.$
* @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures
* as the interface the remote object is exporting.
* @param autostart Disable/Enable auto-starting of services in response to calls on this object.
* Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name
* and is not owned the bus will attempt to start a process to take the name. When disabled an error is
* returned immediately.
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package.
*/
public <I extends DBusInterface> I getPeerRemoteObject(String busname, String objectpath, Class<I> type, boolean autostart) throws DBusException {
if (null == busname) throw new DBusException(getString("nullBusName"));
if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX))
|| busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + busname);
String unique = _dbus.GetNameOwner(busname);
return getRemoteObject(unique, objectpath, type, autostart);
}
/**
* Return a reference to a remote object.
* This method will always refer to the well known name (if given) rather than resolving it to a unique bus name.
* In particular this means that if a process providing the well known name disappears and is taken over by another process
* proxy objects gained by this method will make calls on the new proccess.
*
* @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local")
* or may be a DBus address such as ":1-16".
* @param objectpath The path on which the process is exporting the object.
* @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures
* as the interface the remote object is exporting.
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package.
*/
public <I extends DBusInterface> I getRemoteObject(String busname, String objectpath, Class<I> type) throws DBusException {
return getRemoteObject(busname, objectpath, type, true);
}
/**
* Return a reference to a remote object.
* This method will always refer to the well known name (if given) rather than resolving it to a unique bus name.
* In particular this means that if a process providing the well known name disappears and is taken over by another process
* proxy objects gained by this method will make calls on the new proccess.
*
* @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local")
* or may be a DBus address such as ":1-16".
* @param objectpath The path on which the process is exporting the object.
* @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures
* as the interface the remote object is exporting.
* @param autostart Disable/Enable auto-starting of services in response to calls on this object.
* Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name
* and is not owned the bus will attempt to start a process to take the name. When disabled an error is
* returned immediately.
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package.
*/
@SuppressWarnings("unchecked")
public <I extends DBusInterface> I getRemoteObject(String busname, String objectpath, Class<I> type, boolean autostart) throws DBusException {
if (null == busname) throw new DBusException(getString("nullBusName"));
if (null == objectpath) throw new DBusException(getString("nullObjectPath"));
if (null == type) throw new ClassCastException(getString("notDBusInterface"));
if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX))
|| busname.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + busname);
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + objectpath);
if (!DBusInterface.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusInterface"));
// don't let people import things which don't have a
// valid D-Bus interface name
if (type.getName().equals(type.getSimpleName()))
throw new DBusException(getString("interfaceNotAllowedOutsidePackage"));
RemoteObject ro = new RemoteObject(busname, objectpath, type, autostart);
I i = (I) Proxy.newProxyInstance(type.getClassLoader(),
new Class[]{type}, new RemoteInvocationHandler(this, ro));
importedObjects.put(i, ro);
return i;
}
/**
* Remove a Signal Handler.
* Stops listening for this signal.
*
* @param type The signal to watch for.
* @param source The source of the signal.
* @throws DBusException If listening for the signal on the bus failed.
* @throws ClassCastException If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> void removeSigHandler(Class<T> type, String source, DBusSigHandler<T> handler) throws DBusException {
if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal"));
if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName"));
if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + source);
removeSigHandler(new DBusMatchRule(type, source, null), handler);
}
/**
* Remove a Signal Handler.
* Stops listening for this signal.
*
* @param type The signal to watch for.
* @param source The source of the signal.
* @param object The object emitting the signal.
* @throws DBusException If listening for the signal on the bus failed.
* @throws ClassCastException If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> void removeSigHandler(Class<T> type, String source, DBusInterface object, DBusSigHandler<T> handler) throws DBusException {
if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal"));
if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName"));
if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + source);
String objectpath = importedObjects.get(object).objectpath;
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + objectpath);
removeSigHandler(new DBusMatchRule(type, source, objectpath), handler);
}
protected <T extends DBusSignal> void removeSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException {
SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource());
synchronized (handledSignals) {
Vector<DBusSigHandler<? extends DBusSignal>> v = handledSignals.get(key);
if (null != v) {
v.remove(handler);
if (0 == v.size()) {
handledSignals.remove(key);
try {
_dbus.RemoveMatch(rule.toString());
} catch (NotConnected NC) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, NC);
} catch (DBusExecutionException DBEe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe);
throw new DBusException(DBEe.getMessage());
}
}
}
}
}
/**
* Add a Signal Handler.
* Adds a signal handler to call when a signal is received which matches the specified type, name and source.
*
* @param type The signal to watch for.
* @param source The process which will send the signal. This <b>MUST</b> be a unique bus name and not a well known name.
* @param handler The handler to call when a signal is received.
* @throws DBusException If listening for the signal on the bus failed.
* @throws ClassCastException If type is not a sub-type of DBusSignal.
*/
@SuppressWarnings("unchecked")
public <T extends DBusSignal> void addSigHandler(Class<T> type, String source, DBusSigHandler<T> handler) throws DBusException {
if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal"));
if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName"));
if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + source);
addSigHandler(new DBusMatchRule(type, source, null), (DBusSigHandler<? extends DBusSignal>) handler);
}
/**
* Add a Signal Handler.
* Adds a signal handler to call when a signal is received which matches the specified type, name, source and object.
*
* @param type The signal to watch for.
* @param source The process which will send the signal. This <b>MUST</b> be a unique bus name and not a well known name.
* @param object The object from which the signal will be emitted
* @param handler The handler to call when a signal is received.
* @throws DBusException If listening for the signal on the bus failed.
* @throws ClassCastException If type is not a sub-type of DBusSignal.
*/
@SuppressWarnings("unchecked")
public <T extends DBusSignal> void addSigHandler(Class<T> type, String source, DBusInterface object, DBusSigHandler<T> handler) throws DBusException {
if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal"));
if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName"));
if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidBusName") + source);
String objectpath = importedObjects.get(object).objectpath;
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + objectpath);
addSigHandler(new DBusMatchRule(type, source, objectpath), (DBusSigHandler<? extends DBusSignal>) handler);
}
protected <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException {
try {
_dbus.AddMatch(rule.toString());
} catch (DBusExecutionException DBEe) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe);
throw new DBusException(DBEe.getMessage());
}
SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource());
synchronized (handledSignals) {
Vector<DBusSigHandler<? extends DBusSignal>> v = handledSignals.get(key);
if (null == v) {
v = new Vector<DBusSigHandler<? extends DBusSignal>>();
v.add(handler);
handledSignals.put(key, v);
} else
v.add(handler);
}
}
/**
* Disconnect from the Bus.
* This only disconnects when the last reference to the bus has disconnect called on it
* or has been destroyed.
*/
public void disconnect() {
synchronized (conn) {
synchronized (_reflock) {
if (0 == --_refcount) {
if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting DBusConnection");
// Set all pending messages to have an error.
try {
Error err = new Error(
"org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")});
synchronized (pendingCalls) {
long[] set = pendingCalls.getKeys();
for (long l : set)
if (-1 != l) {
MethodCall m = pendingCalls.remove(l);
if (null != m)
m.setReply(err);
}
pendingCalls = null;
}
synchronized (pendingErrors) {
pendingErrors.add(err);
}
} catch (DBusException DBe) {
}
conn.remove(addr);
super.disconnect();
}
}
}
}
}

View file

@ -1,31 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
/**
* Denotes a class as exportable or a remote interface which can be called.
* <p>
* Any interface which should be exported or imported should extend this
* interface. All public methods from that interface are exported/imported
* with the given method signatures.
* </p>
* <p>
* All method calls on exported objects are run in their own threads.
* Application writers are responsible for any concurrency issues.
* </p>
*/
public interface DBusInterface {
/**
* Returns true on remote objects.
* Local objects implementing this interface MUST return false.
*/
public boolean isRemote();
}

View file

@ -1,28 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Force the interface name to be different to the Java class name.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DBusInterfaceName {
/**
* The replacement interface name.
*/
String value();
}

View file

@ -1,150 +1,184 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.stream.Collectors;
import java.util.Vector;
class DBusMap<K, V> implements Map<K, V> { public class DBusMap<K, V> implements Map<K, V> {
// CHECKSTYLE:OFF
Object[][] entries; Object[][] entries;
// CHECKSTYLE:ON
public DBusMap(Object[][] _entries) {
this.entries = _entries;
}
public DBusMap(Object[][] entries) { @Override
this.entries = entries; public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object _key) {
for (Object[] entry : entries) {
if (Objects.equals(_key, entry[0])) {
return true;
}
}
return false;
}
@Override
public boolean containsValue(Object _value) {
for (Object[] entry : entries) {
if (Objects.equals(_value, entry[1])) {
return true;
}
}
return false;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> s = new LinkedHashSet<>();
for (int i = 0; i < entries.length; i++) {
s.add(new Entry(i));
}
return s;
}
@Override
@SuppressWarnings("unchecked")
public V get(Object _key) {
for (Object[] entry : entries) {
if (_key == entry[0] || _key != null && _key.equals(entry[0])) {
return (V) entry[1];
}
}
return null;
}
@Override
public boolean isEmpty() {
return entries.length == 0;
}
@Override
@SuppressWarnings("unchecked")
public Set<K> keySet() {
Set<K> s = new LinkedHashSet<>();
for (Object[] entry : entries) {
s.add((K) entry[0]);
}
return s;
}
@Override
public V put(K _key, V _value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends K, ? extends V> _t) {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object _key) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return entries.length;
}
@Override
@SuppressWarnings("unchecked")
public Collection<V> values() {
List<V> l = new ArrayList<>();
for (Object[] entry : entries) {
l.add((V) entry[1]);
}
return l;
}
@Override
public int hashCode() {
return Arrays.deepHashCode(entries);
}
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object _o) {
if (null == _o) {
return false;
}
if (!(_o instanceof Map)) {
return false;
}
return ((Map<K, V>) _o).entrySet().equals(entrySet());
}
@Override
public String toString() {
String sb = "{"
+ Arrays.stream(entries).map(e -> e[0] + " => " + e[1])
.collect(Collectors.joining(","))
+ "}";
return sb;
} }
class Entry implements Map.Entry<K, V>, Comparable<Entry> { class Entry implements Map.Entry<K, V>, Comparable<Entry> {
private int entry; private final int entry;
public Entry(int i) { Entry(int _i) {
this.entry = i; this.entry = _i;
} }
public boolean equals(Object o) { @SuppressWarnings("unchecked")
if (null == o) return false; @Override
if (!(o instanceof DBusMap.Entry)) return false; public boolean equals(Object _o) {
return this.entry == ((Entry) o).entry; if (null == _o) {
return false;
}
if (!(_o instanceof DBusMap.Entry)) {
return false;
}
return this.entry == ((Entry) _o).entry;
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public K getKey() { public K getKey() {
return (K) entries[entry][0]; return (K) entries[entry][0];
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public V getValue() { public V getValue() {
return (V) entries[entry][1]; return (V) entries[entry][1];
} }
@Override
public int hashCode() { public int hashCode() {
return entries[entry][0].hashCode(); return entries[entry][0].hashCode();
} }
public V setValue(V value) { @Override
public V setValue(V _value) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public int compareTo(Entry e) { @Override
return entry - e.entry; public int compareTo(Entry _e) {
return entry - _e.entry;
} }
} }
public void clear() {
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key) {
for (int i = 0; i < entries.length; i++)
if (key == entries[i][0] || (key != null && key.equals(entries[i][0])))
return true;
return false;
}
public boolean containsValue(Object value) {
for (int i = 0; i < entries.length; i++)
if (value == entries[i][1] || (value != null && value.equals(entries[i][1])))
return true;
return false;
}
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> s = new TreeSet<Map.Entry<K, V>>();
for (int i = 0; i < entries.length; i++)
s.add(new Entry(i));
return s;
}
@SuppressWarnings("unchecked")
public V get(Object key) {
for (int i = 0; i < entries.length; i++)
if (key == entries[i][0] || (key != null && key.equals(entries[i][0])))
return (V) entries[i][1];
return null;
}
public boolean isEmpty() {
return entries.length == 0;
}
@SuppressWarnings("unchecked")
public Set<K> keySet() {
Set<K> s = new TreeSet<K>();
for (Object[] entry : entries)
s.add((K) entry[0]);
return s;
}
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> t) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public int size() {
return entries.length;
}
@SuppressWarnings("unchecked")
public Collection<V> values() {
List<V> l = new Vector<V>();
for (Object[] entry : entries)
l.add((V) entry[1]);
return l;
}
public int hashCode() {
return Arrays.deepHashCode(entries);
}
@SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (null == o) return false;
if (!(o instanceof Map)) return false;
return ((Map<K, V>) o).entrySet().equals(entrySet());
}
public String toString() {
String s = "{ ";
for (int i = 0; i < entries.length; i++)
s += entries[i][0] + " => " + entries[i][1] + ",";
return s.replaceAll(".$", " }");
}
} }

View file

@ -1,133 +1,270 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import org.freedesktop.dbus.errors.Error;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.messages.MethodReturn;
import org.freedesktop.dbus.utils.DBusNamingUtil;
import org.freedesktop.dbus.utils.Util;
import java.util.HashMap; import java.util.List;
import java.util.Map;
import static org.freedesktop.dbus.Gettext.getString; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* Defined a rule to match a message.<br>
* This is mainly used to handle / take actions when signals arrive.
*/
public class DBusMatchRule { public class DBusMatchRule {
private static final Pattern IFACE_PATTERN = Pattern.compile(".*\\..*");
private static final Map<String, Class<? extends DBusSignal>> SIGNALTYPEMAP = new ConcurrentHashMap<>();
/** Equals operations used in {@link #matches(DBusMatchRule, boolean)} - do not change order! */
private static final List<Function<DBusMatchRule, String>> MATCHRULE_EQUALS_OPERATIONS = List.of(
x -> x.getInterface(),
x -> x.getMember(),
x -> x.getObject(),
x -> x.getSource()
);
/** Equals operations used in {@link #matches(DBusSignal, boolean)} - do not change order! */
private static final List<Function<DBusSignal, String>> SIGNAL_EQUALS_OPERATIONS = List.of(
x -> x.getInterface(),
x -> x.getName(),
x -> x.getPath(),
x -> x.getSource()
);
/* signal, error, method_call, method_reply */ /* signal, error, method_call, method_reply */
private String type; private final String type;
private String iface; private final String iface;
private String member; private final String member;
private String object; private final String object;
private String source; private final String source;
private static HashMap<String, Class<? extends DBusSignal>> signalTypeMap =
new HashMap<String, Class<? extends DBusSignal>>();
static Class<? extends DBusSignal> getCachedSignalType(String type) { public DBusMatchRule(String _type, String _iface, String _member) {
return signalTypeMap.get(type); this(_type, _iface, _member, null);
} }
public DBusMatchRule(String type, String iface, String member) { public DBusMatchRule(String _type, String _iface, String _member, String _object) {
this.type = type; type = _type;
this.iface = iface; iface = _iface;
this.member = member; member = _member;
object = _object;
source = null;
} }
public DBusMatchRule(DBusExecutionException e) throws DBusException { public DBusMatchRule(DBusExecutionException _e) throws DBusException {
this(e.getClass()); this(_e.getClass());
member = null;
type = "error";
} }
public DBusMatchRule(Message m) { public DBusMatchRule(Message _m) {
iface = m.getInterface(); iface = _m.getInterface();
member = m.getName(); source = null;
if (m instanceof DBusSignal) object = null;
member = _m instanceof Error ? null : _m.getName();
if (_m instanceof DBusSignal) {
type = "signal"; type = "signal";
else if (m instanceof Error) { } else if (_m instanceof Error) {
type = "error"; type = "error";
member = null; } else if (_m instanceof MethodCall) {
} else if (m instanceof MethodCall)
type = "method_call"; type = "method_call";
else if (m instanceof MethodReturn) } else if (_m instanceof MethodReturn) {
type = "method_reply"; type = "method_reply";
} else {
type = null;
}
} }
public DBusMatchRule(Class<? extends DBusInterface> c, String method) throws DBusException { public DBusMatchRule(Class<? extends DBusInterface> _c, String _method) throws DBusException {
this(c); this(_c, null, null, "method_call", _method);
member = method;
type = "method_call";
}
public DBusMatchRule(Class<? extends Object> c, String source, String object) throws DBusException {
this(c);
this.source = source;
this.object = object;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public DBusMatchRule(Class<? extends Object> c) throws DBusException { DBusMatchRule(Class<? extends Object> _c, String _source, String _object, String _type, String _member) throws DBusException {
if (DBusInterface.class.isAssignableFrom(c)) { if (DBusInterface.class.isAssignableFrom(_c)) {
if (null != c.getAnnotation(DBusInterfaceName.class)) iface = DBusNamingUtil.getInterfaceName(_c);
iface = c.getAnnotation(DBusInterfaceName.class).value(); assertDBusInterface(iface);
else
iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); member = _member != null ? _member : null;
if (!iface.matches(".*\\..*")) type = _type != null ? _type : null;
throw new DBusException(getString("interfaceMustBeDefinedPackage")); } else if (DBusSignal.class.isAssignableFrom(_c)) {
member = null; if (null == _c.getEnclosingClass()) {
type = null; throw new DBusException("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package.");
} else if (DBusSignal.class.isAssignableFrom(c)) { }
if (null == c.getEnclosingClass()) iface = DBusNamingUtil.getInterfaceName(_c.getEnclosingClass());
throw new DBusException(getString("signalsMustBeMemberOfClass"));
else if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class))
iface = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value();
else
iface = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll(".");
// Don't export things which are invalid D-Bus interfaces // Don't export things which are invalid D-Bus interfaces
if (!iface.matches(".*\\..*")) assertDBusInterface(iface);
throw new DBusException(getString("interfaceMustBeDefinedPackage"));
if (c.isAnnotationPresent(DBusMemberName.class)) member = _member != null ? _member : DBusNamingUtil.getSignalName(_c);
member = c.getAnnotation(DBusMemberName.class).value(); SIGNALTYPEMAP.put(iface + '$' + member, (Class<? extends DBusSignal>) _c);
else type = _type != null ? _type : "signal";
member = c.getSimpleName(); } else if (Error.class.isAssignableFrom(_c)) {
signalTypeMap.put(iface + '$' + member, (Class<? extends DBusSignal>) c); iface = DBusNamingUtil.getInterfaceName(_c);
type = "signal"; assertDBusInterface(iface);
} else if (Error.class.isAssignableFrom(c)) { member = _member != null ? _member : null;
if (null != c.getAnnotation(DBusInterfaceName.class)) type = _type != null ? _type : "error";
iface = c.getAnnotation(DBusInterfaceName.class).value(); } else if (DBusExecutionException.class.isAssignableFrom(_c)) {
else iface = DBusNamingUtil.getInterfaceName(_c);
iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); assertDBusInterface(iface);
if (!iface.matches(".*\\..*")) member = _member != null ? _member : null;
throw new DBusException(getString("interfaceMustBeDefinedPackage")); type = _type != null ? _type : "error";
member = null; } else {
type = "error"; throw new DBusException("Invalid type for match rule: " + _c);
} else if (DBusExecutionException.class.isAssignableFrom(c)) { }
if (null != c.getClass().getAnnotation(DBusInterfaceName.class))
iface = c.getClass().getAnnotation(DBusInterfaceName.class).value(); source = _source;
else object = _object;
iface = AbstractConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll(".");
if (!iface.matches(".*\\..*"))
throw new DBusException(getString("interfaceMustBeDefinedPackage"));
member = null;
type = "error";
} else
throw new DBusException(getString("invalidTypeMatchRule") + c);
} }
public DBusMatchRule(Class<? extends Object> _c, String _source, String _object) throws DBusException {
this(_c, _source, _object, null, null);
}
public DBusMatchRule(Class<? extends Object> _c) throws DBusException {
this(_c, null, null);
}
public static Class<? extends DBusSignal> getCachedSignalType(String _type) {
return SIGNALTYPEMAP.get(_type);
}
void assertDBusInterface(String _str) throws DBusException {
if (!IFACE_PATTERN.matcher(_str).matches()) {
throw new DBusException("DBusInterfaces must be defined in a package.");
}
}
/**
* Checks if the given rule matches with our rule.
* <p>
* Method supports partial matching by setting strict to false.
* Partial means that only the parts of this object are compared to the given
* object which were set (non-null) on ourselves.
* Fields set on the given object but not on ourselves will be ignored.
* </p>
*
* @param _rule rule to compare
* @param _strict true to get an exact match, false to allow partial matches
*
* @return true if matching
*/
public boolean matches(DBusMatchRule _rule, boolean _strict) {
if (_rule == null) {
return false;
}
if (_strict) {
return Util.strEquals(_rule.getInterface(), getInterface())
&& Util.strEquals(_rule.getMember(), getMember())
&& Util.strEquals(_rule.getObject(), getObject())
&& Util.strEquals(_rule.getSource(), getSource());
}
String[] compareVals = new String[] {getInterface(), getMember(), getObject(), getSource()};
for (int i = 0; i < compareVals.length; i++) {
if (compareVals[i] == null) {
continue;
}
Function<DBusMatchRule, String> function = MATCHRULE_EQUALS_OPERATIONS.get(i);
if (!Util.strEquals(compareVals[i], function.apply(_rule))) {
return false;
}
}
return true;
}
/**
* Checks if the given signal matches with our rule.
* <p>
* Method supports partial matching by setting strict to false.
* Partial means that only the parts of this rule are compared to the given
* signal which were set (non-null) on ourselves.
* Fields set on the given signal but not on ourselves will be ignored.
* </p>
*
* @param _signal signal to compare
* @param _strict true to get an exact match, false to allow partial matches
*
* @return true if matching
*/
public boolean matches(DBusSignal _signal, boolean _strict) {
if (_signal == null) {
return false;
}
// _signal.getInterface(), _signal.getName(), _signal.getPath(), _signal.getSource()
if (_strict) {
return Util.strEquals(_signal.getInterface(), getInterface())
&& Util.strEquals(_signal.getName(), getMember())
&& Util.strEquals(_signal.getPath(), getObject())
&& Util.strEquals(_signal.getSource(), getSource());
}
String[] compareVals = new String[] {getInterface(), getMember(), getObject(), getSource()};
for (int i = 0; i < compareVals.length; i++) {
if (compareVals[i] == null) {
continue;
}
Function<DBusSignal, String> function = SIGNAL_EQUALS_OPERATIONS.get(i);
if (!Util.strEquals(compareVals[i], function.apply(_signal))) {
return false;
}
}
return true;
}
@Override
public String toString() { public String toString() {
String s = null; String s = null;
if (null != type) s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'"; if (null != type) {
if (null != member) s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'"; s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'";
if (null != iface) s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'"; }
if (null != source) s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'"; if (null != member) {
if (null != object) s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'"; s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'";
}
if (null != iface) {
s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'";
}
if (null != source) {
s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'";
}
if (null != object) {
s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'";
}
return s; return s;
} }
@Override
public int hashCode() {
return Objects.hash(iface, member, object, source, type);
}
@Override
public boolean equals(Object _obj) {
if (this == _obj) {
return true;
}
if (!(_obj instanceof DBusMatchRule)) {
return false;
}
DBusMatchRule other = (DBusMatchRule) _obj;
return Objects.equals(iface, other.iface) && Objects.equals(member, other.member)
&& Objects.equals(object, other.object) && Objects.equals(source, other.source)
&& Objects.equals(type, other.type);
}
public String getType() { public String getType() {
return type; return type;
} }

View file

@ -1,29 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Force the member (method/signal) name on the bus to be different to the Java name.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DBusMemberName {
/**
* The replacement member name.
*/
String value();
}

View file

@ -0,0 +1,37 @@
package org.freedesktop.dbus;
public class DBusPath implements Comparable<DBusPath> {
private String path;
public DBusPath(String _path) {
this.setPath(_path);
}
public String getPath() {
return path;
}
@Override
public String toString() {
return getPath();
}
@Override
public boolean equals(Object _other) {
return _other instanceof DBusPath && getPath().equals(((DBusPath) _other).getPath());
}
@Override
public int hashCode() {
return getPath().hashCode();
}
@Override
public int compareTo(DBusPath _that) {
return getPath().compareTo(_that.getPath());
}
public void setPath(String _path) {
path = _path;
}
}

View file

@ -1,39 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusException;
/**
* Custom classes may be sent over DBus if they implement this interface.
* <p>
* In addition to the serialize method, classes <b>MUST</b> implement
* a deserialize method which returns null and takes as it's arguments
* all the DBus types the class will be serialied to <i>in order</i> and
* <i>with type parameterisation</i>. They <b>MUST</b> also provide a
* zero-argument constructor.
* </p>
* <p>
* The serialize method should return the class properties you wish to
* serialize, correctly formatted for the wire
* (DBusConnection.convertParameters() can help with this), in order in an
* Object array.
* </p>
* <p>
* The deserialize method will be called once after the zero-argument
* constructor. This should contain all the code to initialise the object
* from the types.
* </p>
*/
public interface DBusSerializable {
public Object[] serialize() throws DBusException;
}

View file

@ -1,27 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
/**
* Handle a signal on DBus.
* All Signal handlers are run in their own Thread.
* Application writers are responsible for managing any concurrency issues.
*/
public interface DBusSigHandler<T extends DBusSignal> {
/**
* Handle a signal.
*
* @param s The signal to handle. If such a class exists, the
* signal will be an instance of the class with the correct type signature.
* Otherwise it will be an instance of DBusSignal
*/
public void handle(T s);
}

View file

@ -1,259 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
public class DBusSignal extends Message {
DBusSignal() {
}
public DBusSignal(String source, String path, String iface, String member, String sig, Object... args) throws DBusException {
super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);
if (null == path || null == member || null == iface)
throw new MessageFormatException(getString("missingPathInterfaceSignal"));
headers.put(Message.HeaderField.PATH, path);
headers.put(Message.HeaderField.MEMBER, member);
headers.put(Message.HeaderField.INTERFACE, iface);
Vector<Object> hargs = new Vector<Object>();
hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}});
hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}});
hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}});
if (null != source) {
headers.put(Message.HeaderField.SENDER, source);
hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}});
}
if (null != sig) {
hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}});
headers.put(Message.HeaderField.SIGNATURE, sig);
setArgs(args);
}
blen = new byte[4];
appendBytes(blen);
append("ua(yv)", ++serial, hargs.toArray());
pad((byte) 8);
long c = bytecounter;
if (null != sig) append(sig, args);
marshallint(bytecounter - c, blen, 0, 4);
bodydone = true;
}
static class internalsig extends DBusSignal {
public internalsig(String source, String objectpath, String type, String name, String sig, Object[] parameters, long serial) throws DBusException {
super(source, objectpath, type, name, sig, parameters, serial);
}
}
private static Map<Class<? extends DBusSignal>, Type[]> typeCache = new HashMap<Class<? extends DBusSignal>, Type[]>();
private static Map<String, Class<? extends DBusSignal>> classCache = new HashMap<String, Class<? extends DBusSignal>>();
private static Map<Class<? extends DBusSignal>, Constructor<? extends DBusSignal>> conCache = new HashMap<Class<? extends DBusSignal>, Constructor<? extends DBusSignal>>();
private static Map<String, String> signames = new HashMap<String, String>();
private static Map<String, String> intnames = new HashMap<String, String>();
private Class<? extends DBusSignal> c;
private boolean bodydone = false;
private byte[] blen;
static void addInterfaceMap(String java, String dbus) {
intnames.put(dbus, java);
}
static void addSignalMap(String java, String dbus) {
signames.put(dbus, java);
}
static DBusSignal createSignal(Class<? extends DBusSignal> c, String source, String objectpath, String sig, long serial, Object... parameters) throws DBusException {
String type = "";
if (null != c.getEnclosingClass()) {
if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class))
type = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value();
else
type = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll(".");
} else
throw new DBusException(getString("signalsMustBeMemberOfClass"));
DBusSignal s = new internalsig(source, objectpath, type, c.getSimpleName(), sig, parameters, serial);
s.c = c;
return s;
}
@SuppressWarnings("unchecked")
private static Class<? extends DBusSignal> createSignalClass(String intname, String signame) throws DBusException {
String name = intname + '$' + signame;
Class<? extends DBusSignal> c = classCache.get(name);
if (null == c) c = DBusMatchRule.getCachedSignalType(name);
if (null != c) return c;
do {
try {
c = (Class<? extends DBusSignal>) Class.forName(name);
} catch (ClassNotFoundException CNFe) {
}
name = name.replaceAll("\\.([^\\.]*)$", "\\$$1");
} while (null == c && name.matches(".*\\..*"));
if (null == c)
throw new DBusException(getString("cannotCreateClassFromSignal") + intname + '.' + signame);
classCache.put(name, c);
return c;
}
@SuppressWarnings("unchecked")
DBusSignal createReal(AbstractConnection conn) throws DBusException {
String intname = intnames.get(getInterface());
String signame = signames.get(getName());
if (null == intname) intname = getInterface();
if (null == signame) signame = getName();
if (null == c)
c = createSignalClass(intname, signame);
if (Debug.debug) Debug.print(Debug.DEBUG, "Converting signal to type: " + c);
Type[] types = typeCache.get(c);
Constructor<? extends DBusSignal> con = conCache.get(c);
if (null == types) {
con = (Constructor<? extends DBusSignal>) c.getDeclaredConstructors()[0];
conCache.put(c, con);
Type[] ts = con.getGenericParameterTypes();
types = new Type[ts.length - 1];
for (int i = 1; i < ts.length; i++)
if (ts[i] instanceof TypeVariable)
for (Type b : ((TypeVariable<GenericDeclaration>) ts[i]).getBounds())
types[i - 1] = b;
else
types[i - 1] = ts[i];
typeCache.put(c, types);
}
try {
DBusSignal s;
Object[] args = Marshalling.deSerializeParameters(getParameters(), types, conn);
if (null == args) s = (DBusSignal) con.newInstance(getPath());
else {
Object[] params = new Object[args.length + 1];
params[0] = getPath();
System.arraycopy(args, 0, params, 1, args.length);
if (Debug.debug)
Debug.print(Debug.DEBUG, "Creating signal of type " + c + " with parameters " + Arrays.deepToString(params));
s = (DBusSignal) con.newInstance(params);
}
s.headers = headers;
s.wiredata = wiredata;
s.bytecounter = wiredata.length;
return s;
} catch (Exception e) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
throw new DBusException(e.getMessage());
}
}
/**
* Create a new signal.
* This contructor MUST be called by all sub classes.
*
* @param objectpath The path to the object this is emitted from.
* @param args The parameters of the signal.
* @throws DBusException This is thrown if the subclass is incorrectly defined.
*/
@SuppressWarnings("unchecked")
protected DBusSignal(String objectpath, Object... args) throws DBusException {
super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);
if (!objectpath.matches(AbstractConnection.OBJECT_REGEX))
throw new DBusException(getString("invalidObjectPath") + objectpath);
Class<? extends DBusSignal> tc = getClass();
String member;
if (tc.isAnnotationPresent(DBusMemberName.class))
member = tc.getAnnotation(DBusMemberName.class).value();
else
member = tc.getSimpleName();
String iface = null;
Class<? extends Object> enc = tc.getEnclosingClass();
if (null == enc ||
!DBusInterface.class.isAssignableFrom(enc) ||
enc.getName().equals(enc.getSimpleName()))
throw new DBusException(getString("signalsMustBeMemberOfClass"));
else if (null != enc.getAnnotation(DBusInterfaceName.class))
iface = enc.getAnnotation(DBusInterfaceName.class).value();
else
iface = AbstractConnection.dollar_pattern.matcher(enc.getName()).replaceAll(".");
headers.put(Message.HeaderField.PATH, objectpath);
headers.put(Message.HeaderField.MEMBER, member);
headers.put(Message.HeaderField.INTERFACE, iface);
Vector<Object> hargs = new Vector<Object>();
hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, objectpath}});
hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}});
hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}});
String sig = null;
if (0 < args.length) {
try {
Type[] types = typeCache.get(tc);
if (null == types) {
Constructor<? extends DBusSignal> con = (Constructor<? extends DBusSignal>) tc.getDeclaredConstructors()[0];
conCache.put(tc, con);
Type[] ts = con.getGenericParameterTypes();
types = new Type[ts.length - 1];
for (int i = 1; i <= types.length; i++)
if (ts[i] instanceof TypeVariable)
types[i - 1] = ((TypeVariable<GenericDeclaration>) ts[i]).getBounds()[0];
else
types[i - 1] = ts[i];
typeCache.put(tc, types);
}
sig = Marshalling.getDBusType(types);
hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}});
headers.put(Message.HeaderField.SIGNATURE, sig);
setArgs(args);
} catch (Exception e) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
throw new DBusException(getString("errorAddSignalParameters") + e.getMessage());
}
}
blen = new byte[4];
appendBytes(blen);
append("ua(yv)", ++serial, hargs.toArray());
pad((byte) 8);
}
void appendbody(AbstractConnection conn) throws DBusException {
if (bodydone) return;
Type[] types = typeCache.get(getClass());
Object[] args = Marshalling.convertParameters(getParameters(), types, conn);
setArgs(args);
String sig = getSig();
long c = bytecounter;
if (null != args && 0 < args.length) append(sig, args);
marshallint(bytecounter - c, blen, 0, 4);
bodydone = true;
}
}

View file

@ -1,122 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
/**
* Provides a long =&gt; MethodCall map which doesn't allocate objects
* on insertion/removal. Keys must be inserted in ascending order.
*/
class EfficientMap {
private long[] kv;
private MethodCall[] vv;
private int start;
private int end;
private int init_size;
public EfficientMap(int initial_size) {
init_size = initial_size;
shrink();
}
private void grow() {
// create new vectors twice as long
long[] oldkv = kv;
kv = new long[oldkv.length * 2];
MethodCall[] oldvv = vv;
vv = new MethodCall[oldvv.length * 2];
// copy start->length to the start of the new vector
System.arraycopy(oldkv, start, kv, 0, oldkv.length - start);
System.arraycopy(oldvv, start, vv, 0, oldvv.length - start);
// copy 0->end to the next part of the new vector
if (end != (oldkv.length - 1)) {
System.arraycopy(oldkv, 0, kv, oldkv.length - start, end + 1);
System.arraycopy(oldvv, 0, vv, oldvv.length - start, end + 1);
}
// reposition pointers
start = 0;
end = oldkv.length;
}
// create a new vector with just the valid keys in and return it
public long[] getKeys() {
int size;
if (start < end) size = end - start;
else size = kv.length - (start - end);
long[] lv = new long[size];
int copya;
if (size > kv.length - start) copya = kv.length - start;
else copya = size;
System.arraycopy(kv, start, lv, 0, copya);
if (copya < size) {
System.arraycopy(kv, 0, lv, copya, size - copya);
}
return lv;
}
private void shrink() {
if (null != kv && kv.length == init_size) return;
// reset to original size
kv = new long[init_size];
vv = new MethodCall[init_size];
start = 0;
end = 0;
}
public void put(long l, MethodCall m) {
// put this at the end
kv[end] = l;
vv[end] = m;
// move the end
if (end == (kv.length - 1)) end = 0;
else end++;
// if we are out of space, grow.
if (end == start) grow();
}
public MethodCall remove(long l) {
// find the item
int pos = find(l);
// if we don't have it return null
if (-1 == pos) return null;
// get the value
MethodCall m = vv[pos];
// set it as unused
vv[pos] = null;
kv[pos] = -1;
// move the pointer to the first full element
while (-1 == kv[start]) {
if (start == (kv.length - 1)) start = 0;
else start++;
// if we have emptied the list, shrink it
if (start == end) {
shrink();
break;
}
}
return m;
}
public boolean contains(long l) {
// check if find succeeds
return -1 != find(l);
}
/* could binary search, but it's probably the first one */
private int find(long l) {
int i = start;
while (i != end && kv[i] != l)
if (i == (kv.length - 1)) i = 0;
else i++;
if (i == end) return -1;
return i;
}
}

View file

@ -1,109 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
/**
* Provides a Message queue which doesn't allocate objects
* on insertion/removal.
*/
class EfficientQueue {
private Message[] mv;
private int start;
private int end;
private int init_size;
public EfficientQueue(int initial_size) {
init_size = initial_size;
shrink();
}
private void grow() {
if (Debug.debug) Debug.print(Debug.DEBUG, "Growing");
// create new vectors twice as long
Message[] oldmv = mv;
mv = new Message[oldmv.length * 2];
// copy start->length to the start of the new vector
System.arraycopy(oldmv, start, mv, 0, oldmv.length - start);
// copy 0->end to the next part of the new vector
if (end != (oldmv.length - 1)) {
System.arraycopy(oldmv, 0, mv, oldmv.length - start, end + 1);
}
// reposition pointers
start = 0;
end = oldmv.length;
}
// create a new vector with just the valid keys in and return it
public Message[] getKeys() {
if (start == end) return new Message[0];
Message[] lv;
if (start < end) {
int size = end - start;
lv = new Message[size];
System.arraycopy(mv, start, lv, 0, size);
} else {
int size = mv.length - start + end;
lv = new Message[size];
System.arraycopy(mv, start, lv, 0, mv.length - start);
System.arraycopy(mv, 0, lv, mv.length - start, end);
}
return lv;
}
private void shrink() {
if (Debug.debug) Debug.print(Debug.DEBUG, "Shrinking");
if (null != mv && mv.length == init_size) return;
// reset to original size
mv = new Message[init_size];
start = 0;
end = 0;
}
public void add(Message m) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Enqueueing Message " + m);
// put this at the end
mv[end] = m;
// move the end
if (end == (mv.length - 1)) end = 0;
else end++;
// if we are out of space, grow.
if (end == start) grow();
}
public Message remove() {
if (start == end) return null;
// find the item
int pos = start;
// get the value
Message m = mv[pos];
// set it as unused
mv[pos] = null;
if (start == (mv.length - 1)) start = 0;
else start++;
if (Debug.debug) Debug.print(Debug.DEBUG, "Dequeueing " + m);
return m;
}
public boolean isEmpty() {
// check if find succeeds
return start == end;
}
public int size() {
if (end >= start)
return end - start;
else
return mv.length - start + end;
}
}

View file

@ -1,144 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import org.freedesktop.dbus.exceptions.NotConnected;
import java.lang.reflect.Constructor;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
/**
* Error messages which can be sent over the bus.
*/
public class Error extends Message {
Error() {
}
public Error(String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException {
this(null, dest, errorName, replyserial, sig, args);
}
public Error(String source, String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException {
super(Message.Endian.BIG, Message.MessageType.ERROR, (byte) 0);
if (null == errorName)
throw new MessageFormatException(getString("missingErrorName"));
headers.put(Message.HeaderField.REPLY_SERIAL, replyserial);
headers.put(Message.HeaderField.ERROR_NAME, errorName);
Vector<Object> hargs = new Vector<Object>();
hargs.add(new Object[]{Message.HeaderField.ERROR_NAME, new Object[]{ArgumentType.STRING_STRING, errorName}});
hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}});
if (null != source) {
headers.put(Message.HeaderField.SENDER, source);
hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}});
}
if (null != dest) {
headers.put(Message.HeaderField.DESTINATION, dest);
hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}});
}
if (null != sig) {
hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}});
headers.put(Message.HeaderField.SIGNATURE, sig);
setArgs(args);
}
byte[] blen = new byte[4];
appendBytes(blen);
append("ua(yv)", serial, hargs.toArray());
pad((byte) 8);
long c = bytecounter;
if (null != sig) append(sig, args);
marshallint(bytecounter - c, blen, 0, 4);
}
public Error(String source, Message m, Throwable e) throws DBusException {
this(source, m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage());
}
public Error(Message m, Throwable e) throws DBusException {
this(m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage());
}
@SuppressWarnings("unchecked")
private static Class<? extends DBusExecutionException> createExceptionClass(String name) {
if (name == "org.freedesktop.DBus.Local.disconnected") return NotConnected.class;
Class<? extends DBusExecutionException> c = null;
do {
try {
c = (Class<? extends org.freedesktop.dbus.exceptions.DBusExecutionException>) Class.forName(name);
} catch (ClassNotFoundException CNFe) {
}
name = name.replaceAll("\\.([^\\.]*)$", "\\$$1");
} while (null == c && name.matches(".*\\..*"));
return c;
}
/**
* Turns this into an exception of the correct type
*/
public DBusExecutionException getException() {
try {
Class<? extends DBusExecutionException> c = createExceptionClass(getName());
if (null == c || !DBusExecutionException.class.isAssignableFrom(c)) c = DBusExecutionException.class;
Constructor<? extends DBusExecutionException> con = c.getConstructor(String.class);
DBusExecutionException ex;
Object[] args = getParameters();
if (null == args || 0 == args.length)
ex = con.newInstance("");
else {
String s = "";
for (Object o : args)
s += o + " ";
ex = con.newInstance(s.trim());
}
ex.setType(getName());
return ex;
} catch (Exception e) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug && null != e.getCause())
Debug.print(Debug.ERR, e.getCause());
DBusExecutionException ex;
Object[] args = null;
try {
args = getParameters();
} catch (Exception ee) {
}
if (null == args || 0 == args.length)
ex = new DBusExecutionException("");
else {
String s = "";
for (Object o : args)
s += o + " ";
ex = new DBusExecutionException(s.trim());
}
ex.setType(getName());
return ex;
}
}
/**
* Throw this as an exception of the correct type
*/
public void throwException() throws DBusExecutionException {
throw getException();
}
}

View file

@ -1,165 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import java.lang.annotation.Annotation;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import static org.freedesktop.dbus.Gettext.getString;
class ExportedObject {
@SuppressWarnings("unchecked")
private String getAnnotations(AnnotatedElement c) {
String ans = "";
for (Annotation a : c.getDeclaredAnnotations()) {
Class t = a.annotationType();
String value = "";
try {
Method m = t.getMethod("value");
value = m.invoke(a).toString();
} catch (NoSuchMethodException NSMe) {
} catch (InvocationTargetException ITe) {
} catch (IllegalAccessException IAe) {
}
ans += " <annotation name=\"" + AbstractConnection.dollar_pattern.matcher(t.getName()).replaceAll(".") + "\" value=\"" + value + "\" />\n";
}
return ans;
}
@SuppressWarnings("unchecked")
private Map<MethodTuple, Method> getExportedMethods(Class c) throws DBusException {
if (DBusInterface.class.equals(c)) return new HashMap<MethodTuple, Method>();
Map<MethodTuple, Method> m = new HashMap<MethodTuple, Method>();
for (Class i : c.getInterfaces())
if (DBusInterface.class.equals(i)) {
// add this class's public methods
if (null != c.getAnnotation(DBusInterfaceName.class)) {
String name = ((DBusInterfaceName) c.getAnnotation(DBusInterfaceName.class)).value();
introspectiondata += " <interface name=\"" + name + "\">\n";
DBusSignal.addInterfaceMap(c.getName(), name);
} else {
// don't let people export things which don't have a
// valid D-Bus interface name
if (c.getName().equals(c.getSimpleName()))
throw new DBusException(getString("interfaceNotAllowedOutsidePackage"));
if (c.getName().length() > DBusConnection.MAX_NAME_LENGTH)
throw new DBusException(getString("introspectInterfaceExceedCharacters") + c.getName());
else
introspectiondata += " <interface name=\"" + AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".") + "\">\n";
}
introspectiondata += getAnnotations(c);
for (Method meth : c.getDeclaredMethods())
if (Modifier.isPublic(meth.getModifiers())) {
String ms = "";
String name;
if (meth.isAnnotationPresent(DBusMemberName.class))
name = meth.getAnnotation(DBusMemberName.class).value();
else
name = meth.getName();
if (name.length() > DBusConnection.MAX_NAME_LENGTH)
throw new DBusException(getString("introspectMethodExceedCharacters") + name);
introspectiondata += " <method name=\"" + name + "\" >\n";
introspectiondata += getAnnotations(meth);
for (Class ex : meth.getExceptionTypes())
if (DBusExecutionException.class.isAssignableFrom(ex))
introspectiondata +=
" <annotation name=\"org.freedesktop.DBus.Method.Error\" value=\"" + AbstractConnection.dollar_pattern.matcher(ex.getName()).replaceAll(".") + "\" />\n";
for (Type pt : meth.getGenericParameterTypes())
for (String s : Marshalling.getDBusType(pt)) {
introspectiondata += " <arg type=\"" + s + "\" direction=\"in\"/>\n";
ms += s;
}
if (!Void.TYPE.equals(meth.getGenericReturnType())) {
if (Tuple.class.isAssignableFrom((Class) meth.getReturnType())) {
ParameterizedType tc = (ParameterizedType) meth.getGenericReturnType();
Type[] ts = tc.getActualTypeArguments();
for (Type t : ts)
if (t != null)
for (String s : Marshalling.getDBusType(t))
introspectiondata += " <arg type=\"" + s + "\" direction=\"out\"/>\n";
} else if (Object[].class.equals(meth.getGenericReturnType())) {
throw new DBusException(getString("cannotIntrospectReturnType"));
} else
for (String s : Marshalling.getDBusType(meth.getGenericReturnType()))
introspectiondata += " <arg type=\"" + s + "\" direction=\"out\"/>\n";
}
introspectiondata += " </method>\n";
m.put(new MethodTuple(name, ms), meth);
}
for (Class sig : c.getDeclaredClasses())
if (DBusSignal.class.isAssignableFrom(sig)) {
String name;
if (sig.isAnnotationPresent(DBusMemberName.class)) {
name = ((DBusMemberName) sig.getAnnotation(DBusMemberName.class)).value();
DBusSignal.addSignalMap(sig.getSimpleName(), name);
} else
name = sig.getSimpleName();
if (name.length() > DBusConnection.MAX_NAME_LENGTH)
throw new DBusException(getString("introspectSignalExceedCharacters") + name);
introspectiondata += " <signal name=\"" + name + "\">\n";
Constructor con = sig.getConstructors()[0];
Type[] ts = con.getGenericParameterTypes();
for (int j = 1; j < ts.length; j++)
for (String s : Marshalling.getDBusType(ts[j]))
introspectiondata += " <arg type=\"" + s + "\" direction=\"out\" />\n";
introspectiondata += getAnnotations(sig);
introspectiondata += " </signal>\n";
}
introspectiondata += " </interface>\n";
} else {
// recurse
m.putAll(getExportedMethods(i));
}
return m;
}
Map<MethodTuple, Method> methods;
Reference<DBusInterface> object;
String introspectiondata;
public ExportedObject(DBusInterface object, boolean weakreferences) throws DBusException {
if (weakreferences)
this.object = new WeakReference<DBusInterface>(object);
else
this.object = new StrongReference<DBusInterface>(object);
introspectiondata = "";
methods = getExportedMethods(object.getClass());
introspectiondata +=
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n" +
" <method name=\"Introspect\">\n" +
" <arg type=\"s\" direction=\"out\"/>\n" +
" </method>\n" +
" </interface>\n";
introspectiondata +=
" <interface name=\"org.freedesktop.DBus.Peer\">\n" +
" <method name=\"Ping\">\n" +
" </method>\n" +
" </interface>\n";
}
}

View file

@ -0,0 +1,64 @@
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.MarshallingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* Represents a FileDescriptor to be passed over the bus. Can be created from
* either an integer(gotten through some JNI/JNA/JNR call) or from a
* java.io.FileDescriptor.
*
*/
public class FileDescriptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final int fd;
public FileDescriptor(int _fd) {
fd = _fd;
}
// TODO this should have a better exception?
public FileDescriptor(java.io.FileDescriptor _data) throws MarshallingException {
fd = getFileDescriptor(_data);
}
// TODO this should have a better exception?
public java.io.FileDescriptor toJavaFileDescriptor() throws MarshallingException {
return createFileDescriptorByReflection(fd);
}
public int getIntFileDescriptor() {
return fd;
}
private int getFileDescriptor(java.io.FileDescriptor _data) throws MarshallingException {
Field declaredField;
try {
declaredField = _data.getClass().getDeclaredField("fd");
declaredField.setAccessible(true);
return declaredField.getInt(_data);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException _ex) {
logger.error("Could not get filedescriptor by reflection.", _ex);
throw new MarshallingException("Could not get member 'fd' of FileDescriptor by reflection!", _ex);
}
}
private java.io.FileDescriptor createFileDescriptorByReflection(long _demarshallint) throws MarshallingException {
try {
Constructor<java.io.FileDescriptor> constructor = java.io.FileDescriptor.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
return constructor.newInstance((int) _demarshallint);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException _ex) {
logger.error("Could not create new FileDescriptor instance by reflection.", _ex);
throw new MarshallingException("Could not create new FileDescriptor instance by reflection", _ex);
}
}
}

View file

@ -1,30 +0,0 @@
/*
* Pescetti Pseudo-Duplimate Generator
*
* Copyright (C) 2007 Matthew Johnson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License Version 2 as published by
* the Free Software Foundation. This program 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; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* To Contact me, please email src@matthew.ath.cx
*
*/
package org.freedesktop.dbus;
import java.util.ResourceBundle;
public class Gettext {
private static ResourceBundle myResources =
ResourceBundle.getBundle("en_US");
public static String getString(String s) {
return myResources.getString(s);
}
}

View file

@ -1,20 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusException;
class InternalSignal extends DBusSignal {
public InternalSignal(String source, String objectpath, String name, String iface, String sig, long serial, Object... parameters) throws DBusException {
super(objectpath, iface, name, sig, parameters);
this.serial = serial;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,194 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import cx.ath.matthew.utils.Hexdump;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MessageProtocolVersionException;
import org.freedesktop.dbus.exceptions.MessageTypeException;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.text.MessageFormat;
import static org.freedesktop.dbus.Gettext.getString;
public class MessageReader {
private InputStream in;
private byte[] buf = null;
private byte[] tbuf = null;
private byte[] header = null;
private byte[] body = null;
private int[] len = new int[4];
public MessageReader(InputStream in) {
this.in = new BufferedInputStream(in);
}
public Message readMessage() throws IOException, DBusException {
int rv;
/* Read the 12 byte fixed header, retrying as neccessary */
if (null == buf) {
buf = new byte[12];
len[0] = 0;
}
if (len[0] < 12) {
try {
rv = in.read(buf, len[0], 12 - len[0]);
} catch (SocketTimeoutException STe) {
return null;
}
if (-1 == rv) throw new EOFException(getString("transportReturnedEOF"));
len[0] += rv;
}
if (len[0] == 0) return null;
if (len[0] < 12) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[0] + " of 12 bytes of header");
return null;
}
/* Parse the details from the header */
byte endian = buf[0];
byte type = buf[1];
byte protover = buf[3];
if (protover > Message.PROTOCOL) {
buf = null;
throw new MessageProtocolVersionException(MessageFormat.format(getString("protocolVersionUnsupported"), new Object[]{protover}));
}
/* Read the length of the variable header */
if (null == tbuf) {
tbuf = new byte[4];
len[1] = 0;
}
if (len[1] < 4) {
try {
rv = in.read(tbuf, len[1], 4 - len[1]);
} catch (SocketTimeoutException STe) {
return null;
}
if (-1 == rv) throw new EOFException(getString("transportReturnedEOF"));
len[1] += rv;
}
if (len[1] < 4) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[1] + " of 4 bytes of header");
return null;
}
/* Parse the variable header length */
int headerlen = 0;
if (null == header) {
headerlen = (int) Message.demarshallint(tbuf, 0, endian, 4);
if (0 != headerlen % 8)
headerlen += 8 - (headerlen % 8);
} else
headerlen = header.length - 8;
/* Read the variable header */
if (null == header) {
header = new byte[headerlen + 8];
System.arraycopy(tbuf, 0, header, 0, 4);
len[2] = 0;
}
if (len[2] < headerlen) {
try {
rv = in.read(header, 8 + len[2], headerlen - len[2]);
} catch (SocketTimeoutException STe) {
return null;
}
if (-1 == rv) throw new EOFException(getString("transportReturnedEOF"));
len[2] += rv;
}
if (len[2] < headerlen) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[2] + " of " + headerlen + " bytes of header");
return null;
}
/* Read the body */
int bodylen = 0;
if (null == body) bodylen = (int) Message.demarshallint(buf, 4, endian, 4);
if (null == body) {
body = new byte[bodylen];
len[3] = 0;
}
if (len[3] < body.length) {
try {
rv = in.read(body, len[3], body.length - len[3]);
} catch (SocketTimeoutException STe) {
return null;
}
if (-1 == rv) throw new EOFException(getString("transportReturnedEOF"));
len[3] += rv;
}
if (len[3] < body.length) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[3] + " of " + body.length + " bytes of body");
return null;
}
Message m;
switch (type) {
case Message.MessageType.METHOD_CALL:
m = new MethodCall();
break;
case Message.MessageType.METHOD_RETURN:
m = new MethodReturn();
break;
case Message.MessageType.SIGNAL:
m = new DBusSignal();
break;
case Message.MessageType.ERROR:
m = new Error();
break;
default:
throw new MessageTypeException(MessageFormat.format(getString("messageTypeUnsupported"), new Object[]{type}));
}
if (Debug.debug) {
Debug.print(Debug.VERBOSE, Hexdump.format(buf));
Debug.print(Debug.VERBOSE, Hexdump.format(tbuf));
Debug.print(Debug.VERBOSE, Hexdump.format(header));
Debug.print(Debug.VERBOSE, Hexdump.format(body));
}
try {
m.populate(buf, header, body);
} catch (DBusException DBe) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
buf = null;
tbuf = null;
body = null;
header = null;
throw DBe;
} catch (RuntimeException Re) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Re);
buf = null;
tbuf = null;
body = null;
header = null;
throw Re;
}
if (Debug.debug) {
Debug.print(Debug.INFO, "=> " + m);
}
buf = null;
tbuf = null;
body = null;
header = null;
return m;
}
public void close() throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Reader");
in.close();
}
}

View file

@ -1,67 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import cx.ath.matthew.unix.USOutputStream;
import cx.ath.matthew.utils.Hexdump;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class MessageWriter {
private OutputStream out;
private boolean isunix;
public MessageWriter(OutputStream out) {
this.out = out;
this.isunix = false;
try {
if (out instanceof USOutputStream)
this.isunix = true;
} catch (Throwable t) {
}
if (!this.isunix)
this.out = new BufferedOutputStream(this.out);
}
public void writeMessage(Message m) throws IOException {
if (Debug.debug) {
Debug.print(Debug.INFO, "<= " + m);
}
if (null == m) return;
if (null == m.getWireData()) {
if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!");
return;
}
if (isunix) {
if (Debug.debug) {
Debug.print(Debug.DEBUG, "Writing all " + m.getWireData().length + " buffers simultaneously to Unix Socket");
for (byte[] buf : m.getWireData())
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
}
((USOutputStream) out).write(m.getWireData());
} else
for (byte[] buf : m.getWireData()) {
if (Debug.debug)
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
if (null == buf) break;
out.write(buf);
}
out.flush();
}
public void close() throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Writer");
out.close();
}
}

View file

@ -1,137 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import cx.ath.matthew.utils.Hexdump;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
public class MethodCall extends Message {
MethodCall() {
}
public MethodCall(String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException {
this(null, dest, path, iface, member, flags, sig, args);
}
public MethodCall(String source, String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException {
super(Message.Endian.BIG, Message.MessageType.METHOD_CALL, flags);
if (null == member || null == path)
throw new MessageFormatException(getString("missingDestinationPathFunction"));
headers.put(Message.HeaderField.PATH, path);
headers.put(Message.HeaderField.MEMBER, member);
Vector<Object> hargs = new Vector<Object>();
hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}});
if (null != source) {
headers.put(Message.HeaderField.SENDER, source);
hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}});
}
if (null != dest) {
headers.put(Message.HeaderField.DESTINATION, dest);
hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}});
}
if (null != iface) {
hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}});
headers.put(Message.HeaderField.INTERFACE, iface);
}
hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}});
if (null != sig) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Appending arguments with signature: " + sig);
hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}});
headers.put(Message.HeaderField.SIGNATURE, sig);
setArgs(args);
}
byte[] blen = new byte[4];
appendBytes(blen);
append("ua(yv)", serial, hargs.toArray());
pad((byte) 8);
long c = bytecounter;
if (null != sig) append(sig, args);
if (Debug.debug)
Debug.print(Debug.DEBUG, "Appended body, type: " + sig + " start: " + c + " end: " + bytecounter + " size: " + (bytecounter - c));
marshallint(bytecounter - c, blen, 0, 4);
if (Debug.debug) Debug.print("marshalled size (" + blen + "): " + Hexdump.format(blen));
}
private static long REPLY_WAIT_TIMEOUT = 20000;
/**
* Set the default timeout for method calls.
* Default is 20s.
*
* @param timeout New timeout in ms.
*/
public static void setDefaultTimeout(long timeout) {
REPLY_WAIT_TIMEOUT = timeout;
}
Message reply = null;
public synchronized boolean hasReply() {
return null != reply;
}
/**
* Block (if neccessary) for a reply.
*
* @param timeout The length of time to block before timing out (ms).
* @return The reply to this MethodCall, or null if a timeout happens.
*/
public synchronized Message getReply(long timeout) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this);
if (null != reply) return reply;
try {
wait(timeout);
return reply;
} catch (InterruptedException Ie) {
return reply;
}
}
/**
* Block (if neccessary) for a reply.
* Default timeout is 20s, or can be configured with setDefaultTimeout()
*
* @return The reply to this MethodCall, or null if a timeout happens.
*/
public synchronized Message getReply() {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this);
if (null != reply) return reply;
try {
wait(REPLY_WAIT_TIMEOUT);
return reply;
} catch (InterruptedException Ie) {
return reply;
}
}
protected synchronized void setReply(Message reply) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting reply to " + this + " to " + reply);
this.reply = reply;
notifyAll();
}
}

View file

@ -1,77 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusException;
import java.util.Vector;
public class MethodReturn extends Message {
MethodReturn() {
}
public MethodReturn(String dest, long replyserial, String sig, Object... args) throws DBusException {
this(null, dest, replyserial, sig, args);
}
public MethodReturn(String source, String dest, long replyserial, String sig, Object... args) throws DBusException {
super(Message.Endian.BIG, Message.MessageType.METHOD_RETURN, (byte) 0);
headers.put(Message.HeaderField.REPLY_SERIAL, replyserial);
Vector<Object> hargs = new Vector<Object>();
hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}});
if (null != source) {
headers.put(Message.HeaderField.SENDER, source);
hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}});
}
if (null != dest) {
headers.put(Message.HeaderField.DESTINATION, dest);
hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}});
}
if (null != sig) {
hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}});
headers.put(Message.HeaderField.SIGNATURE, sig);
setArgs(args);
}
byte[] blen = new byte[4];
appendBytes(blen);
append("ua(yv)", serial, hargs.toArray());
pad((byte) 8);
long c = bytecounter;
if (null != sig) append(sig, args);
marshallint(bytecounter - c, blen, 0, 4);
}
public MethodReturn(MethodCall mc, String sig, Object... args) throws DBusException {
this(null, mc, sig, args);
}
public MethodReturn(String source, MethodCall mc, String sig, Object... args) throws DBusException {
this(source, mc.getSource(), mc.getSerial(), sig, args);
this.call = mc;
}
MethodCall call;
public MethodCall getCall() {
return call;
}
protected void setCall(MethodCall call) {
this.call = call;
}
}

View file

@ -1,37 +1,52 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class MethodTuple { import java.util.Objects;
String name;
String sig;
public MethodTuple(String name, String sig) { public class MethodTuple {
this.name = name; private final Logger logger = LoggerFactory.getLogger(getClass());
if (null != sig)
this.sig = sig; private final String name;
else private final String sig;
this.sig = "";
if (Debug.debug) Debug.print(Debug.VERBOSE, "new MethodTuple(" + this.name + ", " + this.sig + ")"); public MethodTuple(String _name, String _sig) {
} name = _name;
if (null != _sig) {
public boolean equals(Object o) { sig = _sig;
return o.getClass().equals(MethodTuple.class) } else {
&& ((MethodTuple) o).name.equals(this.name) sig = "";
&& ((MethodTuple) o).sig.equals(this.sig); }
logger.trace("new MethodTuple({}, {})", name, sig);
} }
@Override
public int hashCode() { public int hashCode() {
return name.hashCode() + sig.hashCode(); return Objects.hash(name, sig);
}
@Override
public boolean equals(Object _obj) {
if (this == _obj) {
return true;
}
if (!(_obj instanceof MethodTuple)) {
return false;
}
MethodTuple other = (MethodTuple) _obj;
return Objects.equals(name, other.name) && Objects.equals(sig, other.sig);
}
public Logger getLogger() {
return logger;
}
public String getName() {
return name;
}
public String getSig() {
return sig;
} }
} }

View file

@ -1,22 +1,19 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
class ObjectPath extends Path { public class ObjectPath extends DBusPath {
public String source; private String source;
// public DBusConnection conn; public ObjectPath(String _source, String _path) {
public ObjectPath(String source, String path/*, DBusConnection conn*/) { super(_path);
super(path); this.source = _source;
this.source = source;
// this.conn = conn;
} }
public String getSource() {
return source;
}
public void setSource(String _source) {
source = _source;
}
} }

View file

@ -1,158 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import java.util.regex.Pattern;
/**
* Keeps track of the exported objects for introspection data
*/
class ObjectTree {
class TreeNode {
String name;
ExportedObject object;
String data;
TreeNode right;
TreeNode down;
public TreeNode(String name) {
this.name = name;
}
public TreeNode(String name, ExportedObject object, String data) {
this.name = name;
this.object = object;
this.data = data;
}
}
private TreeNode root;
public ObjectTree() {
root = new TreeNode("");
}
public static final Pattern slashpattern = Pattern.compile("/");
private TreeNode recursiveFind(TreeNode current, String path) {
if ("/".equals(path)) return current;
String[] elements = path.split("/", 2);
// this is us or a parent node
if (path.startsWith(current.name)) {
// this is us
if (path.equals(current.name)) {
return current;
}
// recurse down
else {
if (current.down == null)
return null;
else return recursiveFind(current.down, elements[1]);
}
} else if (current.right == null) {
return null;
} else if (0 > current.right.name.compareTo(elements[0])) {
return null;
}
// recurse right
else {
return recursiveFind(current.right, path);
}
}
private TreeNode recursiveAdd(TreeNode current, String path, ExportedObject object, String data) {
String[] elements = slashpattern.split(path, 2);
// this is us or a parent node
if (path.startsWith(current.name)) {
// this is us
if (1 == elements.length || "".equals(elements[1])) {
current.object = object;
current.data = data;
}
// recurse down
else {
if (current.down == null) {
String[] el = elements[1].split("/", 2);
current.down = new TreeNode(el[0]);
}
current.down = recursiveAdd(current.down, elements[1], object, data);
}
}
// need to create a new sub-tree on the end
else if (current.right == null) {
current.right = new TreeNode(elements[0]);
current.right = recursiveAdd(current.right, path, object, data);
}
// need to insert here
else if (0 > current.right.name.compareTo(elements[0])) {
TreeNode t = new TreeNode(elements[0]);
t.right = current.right;
current.right = t;
current.right = recursiveAdd(current.right, path, object, data);
}
// recurse right
else {
current.right = recursiveAdd(current.right, path, object, data);
}
return current;
}
public void add(String path, ExportedObject object, String data) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Adding " + path + " to object tree");
root = recursiveAdd(root, path, object, data);
}
public void remove(String path) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Removing " + path + " from object tree");
TreeNode t = recursiveFind(root, path);
t.object = null;
t.data = null;
}
public String Introspect(String path) {
TreeNode t = recursiveFind(root, path);
if (null == t) return null;
StringBuilder sb = new StringBuilder();
sb.append("<node name=\"");
sb.append(path);
sb.append("\">\n");
if (null != t.data) sb.append(t.data);
t = t.down;
while (null != t) {
sb.append("<node name=\"");
sb.append(t.name);
sb.append("\"/>\n");
t = t.right;
}
sb.append("</node>");
return sb.toString();
}
private String recursivePrint(TreeNode current) {
String s = "";
if (null != current) {
s += current.name;
if (null != current.object)
s += "*";
if (null != current.down)
s += "/{" + recursivePrint(current.down) + "}";
if (null != current.right)
s += ", " + recursivePrint(current.right);
}
return s;
}
public String toString() {
return recursivePrint(root);
}
}

View file

@ -1,39 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
public class Path implements Comparable<Path> {
protected String path;
public Path(String path) {
this.path = path;
}
public String getPath() {
return path;
}
public String toString() {
return path;
}
public boolean equals(Object other) {
return (other instanceof Path) && path.equals(((Path) other).path);
}
public int hashCode() {
return path.hashCode();
}
public int compareTo(Path that) {
return path.compareTo(that.path);
}
}

View file

@ -1,29 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Position annotation, to annotate Struct fields
* to be sent over DBus.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Position {
/**
* The order of this field in the Struct.
*/
int value();
}

View file

@ -1,187 +1,216 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug; import org.freedesktop.dbus.annotations.MethodNoReply;
import org.freedesktop.DBus; import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.errors.Error;
import org.freedesktop.dbus.errors.NoReply;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.NotConnected; import org.freedesktop.dbus.exceptions.NotConnected;
import org.freedesktop.dbus.interfaces.CallbackHandler;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.utils.DBusNamingUtil;
import org.freedesktop.dbus.utils.LoggingHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
import static org.freedesktop.dbus.Gettext.getString; public class RemoteInvocationHandler implements InvocationHandler {
public static final int CALL_TYPE_SYNC = 0;
class RemoteInvocationHandler implements InvocationHandler { public static final int CALL_TYPE_ASYNC = 1;
public static final int CALL_TYPE_SYNC = 0;
public static final int CALL_TYPE_ASYNC = 1;
public static final int CALL_TYPE_CALLBACK = 2; public static final int CALL_TYPE_CALLBACK = 2;
public static Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException { private static final Logger LOGGER = LoggerFactory.getLogger(RemoteInvocationHandler.class);
Class<? extends Object> c = m.getReturnType(); // CHECKSTYLE:OFF
AbstractConnection conn;
RemoteObject remote;
// CHECKSTYLE:ON
if (null == rp) { public RemoteInvocationHandler(AbstractConnection _conn, RemoteObject _remote) {
if (null == c || Void.TYPE.equals(c)) return null; this.remote = _remote;
else throw new DBusExecutionException(getString("voidReturnType")); this.conn = _conn;
}
public RemoteObject getRemote() {
return remote;
}
@Override
public Object invoke(Object _proxy, Method _method, Object[] _args) throws Throwable {
if (_method.getName().equals("isRemote")) {
return true;
} else if (_method.getName().equals("getObjectPath")) {
return remote.getObjectPath();
} else if (_method.getName().equals("clone")) {
return null;
} else if (_method.getName().equals("equals")) {
try {
if (1 == _args.length) {
return Boolean.valueOf(_args[0] != null && remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(_args[0])).remote));
}
} catch (IllegalArgumentException _exIa) {
return Boolean.FALSE;
}
} else if (_method.getName().equals("finalize")) {
return null;
} else if (_method.getName().equals("getClass")) {
return DBusInterface.class;
} else if (_method.getName().equals("hashCode")) {
return remote.hashCode();
} else if (_method.getName().equals("notify")) {
remote.notify();
return null;
} else if (_method.getName().equals("notifyAll")) {
remote.notifyAll();
return null;
} else if (_method.getName().equals("wait")) {
if (0 == _args.length) {
remote.wait();
} else if (1 == _args.length && _args[0] instanceof Long) {
remote.wait((Long) _args[0]);
} else if (2 == _args.length && _args[0] instanceof Long && _args[1] instanceof Integer) {
remote.wait((Long) _args[0], (Integer) _args[1]);
}
if (_args.length <= 2) {
return null;
}
} else if (_method.getName().equals("toString")) {
return remote.toString();
}
return executeRemoteMethod(remote, _method, conn, CALL_TYPE_SYNC, null, _args);
}
// CHECKSTYLE:ON
public static Object convertRV(String _sig, Object[] _rp, Method _m, AbstractConnection _conn) throws DBusException {
Class<? extends Object> c = _m.getReturnType();
Object[] rp = _rp;
if (rp == null) {
if (null == c || Void.TYPE.equals(c)) {
return null;
} else {
throw new DBusException("Wrong return type (got void, expected a value)");
}
} else { } else {
try { try {
if (Debug.debug) LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> LOGGER.trace("Converting return parameters from {} to type {}",
Debug.print(Debug.VERBOSE, "Converting return parameters from " + Arrays.deepToString(rp) + " to type " + m.getGenericReturnType()); Arrays.deepToString(_rp), _m.getGenericReturnType()));
rp = Marshalling.deSerializeParameters(rp,
new Type[]{m.getGenericReturnType()}, conn); rp = Marshalling.deSerializeParameters(rp, new Type[] {
} catch (Exception e) { _m.getGenericReturnType()
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); }, _conn);
throw new DBusExecutionException(MessageFormat.format(getString("invalidReturnType"), new Object[]{e.getMessage()})); } catch (Exception _ex) {
LOGGER.debug("Wrong return type.", _ex);
throw new DBusException(String.format("Wrong return type (failed to de-serialize correct types: %s )", _ex.getMessage()));
} }
} }
switch (rp.length) { switch (rp.length) {
case 0: case 0:
if (null == c || Void.TYPE.equals(c)) if (null == c || Void.TYPE.equals(c)) {
return null; return null;
else throw new DBusExecutionException(getString("voidReturnType")); } else {
case 1: throw new DBusException("Wrong return type (got void, expected a value)");
return rp[0]; }
default: case 1:
return rp[0];
default:
// check we are meant to return multiple values // check we are meant to return multiple values
if (!Tuple.class.isAssignableFrom(c)) if (!Tuple.class.isAssignableFrom(c)) {
throw new DBusExecutionException(getString("tupleReturnType")); throw new DBusException("Wrong return type (not expecting Tuple)");
}
Constructor<? extends Object> cons = c.getConstructors()[0]; Constructor<? extends Object> cons = c.getConstructors()[0];
try { try {
return cons.newInstance(rp); return cons.newInstance(rp);
} catch (Exception e) { } catch (Exception _ex) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); LOGGER.debug("", _ex);
throw new DBusException(e.getMessage()); throw new DBusException(_ex.getMessage());
} }
} }
} }
@SuppressWarnings("unchecked") public static Object executeRemoteMethod(RemoteObject _ro, Method _m, AbstractConnection _conn, int _syncmethod, CallbackHandler<?> _callback, Object... _args) throws DBusException {
public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, int syncmethod, CallbackHandler callback, Object... args) throws DBusExecutionException { Type[] ts = _m.getGenericParameterTypes();
Type[] ts = m.getGenericParameterTypes();
String sig = null; String sig = null;
if (ts.length > 0) try { Object[] args = _args;
sig = Marshalling.getDBusType(ts); if (ts.length > 0) {
args = Marshalling.convertParameters(args, ts, conn); try {
} catch (DBusException DBe) { sig = Marshalling.getDBusType(ts);
throw new DBusExecutionException(getString("contructDBusTypeFailure") + DBe.getMessage()); args = Marshalling.convertParameters(args, ts, _conn);
} catch (DBusException _ex) {
throw new DBusExecutionException("Failed to construct D-Bus type: " + _ex.getMessage());
}
} }
MethodCall call; MethodCall call;
byte flags = 0; byte flags = 0;
if (!ro.autostart) flags |= Message.Flags.NO_AUTO_START; if (!_ro.isAutostart()) {
if (syncmethod == CALL_TYPE_ASYNC) flags |= Message.Flags.ASYNC; flags |= Message.Flags.NO_AUTO_START;
if (m.isAnnotationPresent(DBus.Method.NoReply.class)) flags |= Message.Flags.NO_REPLY_EXPECTED; }
try { if (_syncmethod == CALL_TYPE_ASYNC) {
String name; flags |= Message.Flags.ASYNC;
if (m.isAnnotationPresent(DBusMemberName.class)) }
name = m.getAnnotation(DBusMemberName.class).value(); if (_m.isAnnotationPresent(MethodNoReply.class)) {
else flags |= Message.Flags.NO_REPLY_EXPECTED;
name = m.getName(); }
if (null == ro.iface) try {
call = new MethodCall(ro.busname, ro.objectpath, null, name, flags, sig, args); String name = DBusNamingUtil.getMethodName(_m);
else { if (null == _ro.getInterface()) {
if (null != ro.iface.getAnnotation(DBusInterfaceName.class)) { call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), null, name, flags, sig, args);
call = new MethodCall(ro.busname, ro.objectpath, ro.iface.getAnnotation(DBusInterfaceName.class).value(), name, flags, sig, args); } else {
} else String iface = DBusNamingUtil.getInterfaceName(_ro.getInterface());
call = new MethodCall(ro.busname, ro.objectpath, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), name, flags, sig, args); call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), iface, name, flags, sig, args);
} }
} catch (DBusException DBe) { } catch (DBusException _ex) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); LOGGER.debug("Failed to construct outgoing method call.", _ex);
throw new DBusExecutionException(getString("constructOutgoingMethodCallFailure") + DBe.getMessage()); throw new DBusExecutionException("Failed to construct outgoing method call: " + _ex.getMessage());
}
if (!_conn.isConnected()) {
throw new NotConnected("Not Connected");
} }
if (null == conn.outgoing) throw new NotConnected(getString("notConnected"));
switch (syncmethod) { switch (_syncmethod) {
case CALL_TYPE_ASYNC: case CALL_TYPE_ASYNC:
conn.queueOutgoing(call); _conn.sendMessage(call);
return new DBusAsyncReply(call, m, conn); return new DBusAsyncReply<>(call, _m, _conn);
case CALL_TYPE_CALLBACK: case CALL_TYPE_CALLBACK:
synchronized (conn.pendingCallbacks) { _conn.queueCallback(call, _m, _callback);
if (Debug.debug) Debug.print(Debug.VERBOSE, "Queueing Callback " + callback + " for " + call); _conn.sendMessage(call);
conn.pendingCallbacks.put(call, callback);
conn.pendingCallbackReplys.put(call, new DBusAsyncReply(call, m, conn));
}
conn.queueOutgoing(call);
return null; return null;
case CALL_TYPE_SYNC: case CALL_TYPE_SYNC:
conn.queueOutgoing(call); _conn.sendMessage(call);
break; break;
} }
// get reply // get reply
if (m.isAnnotationPresent(DBus.Method.NoReply.class)) return null; if (_m.isAnnotationPresent(MethodNoReply.class)) {
return null;
}
Message reply = call.getReply(); Message reply = call.getReply();
if (null == reply) throw new DBus.Error.NoReply(getString("notReplyWithSpecifiedTime")); if (null == reply) {
throw new NoReply("No reply within specified time");
}
if (reply instanceof Error) if (reply instanceof Error) {
((Error) reply).throwException(); ((Error) reply).throwException();
}
try { try {
return convertRV(reply.getSig(), reply.getParameters(), m, conn); return convertRV(reply.getSig(), reply.getParameters(), _m, _conn);
} catch (DBusException e) { } catch (DBusException _ex) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); LOGGER.debug("", _ex);
throw new DBusExecutionException(e.getMessage()); throw new DBusExecutionException(_ex.getMessage());
} }
} }
AbstractConnection conn;
RemoteObject remote;
public RemoteInvocationHandler(AbstractConnection conn, RemoteObject remote) {
this.remote = remote;
this.conn = conn;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("isRemote")) return true;
else if (method.getName().equals("clone")) return null;
else if (method.getName().equals("equals")) {
try {
if (1 == args.length)
return new Boolean(remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(args[0])).remote));
} catch (IllegalArgumentException IAe) {
return Boolean.FALSE;
}
} else if (method.getName().equals("finalize")) return null;
else if (method.getName().equals("getClass")) return DBusInterface.class;
else if (method.getName().equals("hashCode")) return remote.hashCode();
else if (method.getName().equals("notify")) {
remote.notify();
return null;
} else if (method.getName().equals("notifyAll")) {
remote.notifyAll();
return null;
} else if (method.getName().equals("wait")) {
if (0 == args.length) remote.wait();
else if (1 == args.length
&& args[0] instanceof Long) remote.wait((Long) args[0]);
else if (2 == args.length
&& args[0] instanceof Long
&& args[1] instanceof Integer)
remote.wait((Long) args[0], (Integer) args[1]);
if (args.length <= 2)
return null;
} else if (method.getName().equals("toString"))
return remote.toString();
return executeRemoteMethod(remote, method, conn, CALL_TYPE_SYNC, null, args);
}
} }

View file

@ -1,51 +1,52 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
class RemoteObject { import org.freedesktop.dbus.interfaces.DBusInterface;
String busname;
String objectpath;
Class<? extends DBusInterface> iface;
boolean autostart;
public RemoteObject(String busname, String objectpath, Class<? extends DBusInterface> iface, boolean autostart) { public class RemoteObject {
this.busname = busname; private final String busname;
this.objectpath = objectpath; private final String objectpath;
this.iface = iface; private final Class<? extends DBusInterface> iface;
this.autostart = autostart; private final boolean autostart;
public RemoteObject(String _busname, String _objectpath, Class<? extends DBusInterface> _iface, boolean _autostart) {
this.busname = _busname;
this.objectpath = _objectpath;
this.iface = _iface;
this.autostart = _autostart;
} }
public boolean equals(Object o) { @Override
if (!(o instanceof RemoteObject)) return false; public boolean equals(Object _o) {
RemoteObject them = (RemoteObject) o; if (!(_o instanceof RemoteObject)) {
return false;
}
RemoteObject them = (RemoteObject) _o;
if (!them.objectpath.equals(this.objectpath)) return false; if (!them.objectpath.equals(this.objectpath)) {
return false;
if (null == this.busname && null != them.busname) return false; } else if (null == this.busname && null != them.busname) {
if (null != this.busname && null == them.busname) return false; return false;
if (null != them.busname && !them.busname.equals(this.busname)) return false; } else if (null != this.busname && null == them.busname) {
return false;
if (null == this.iface && null != them.iface) return false; } else if (null != them.busname && !them.busname.equals(this.busname)) {
if (null != this.iface && null == them.iface) return false; return false;
if (null != them.iface && !them.iface.equals(this.iface)) return false; } else if (null == this.iface && null != them.iface) {
return false;
} else if (null != this.iface && null == them.iface) {
return false;
} else if (null != them.iface && !them.iface.equals(this.iface)) {
return false;
}
return true; return true;
} }
@Override
public int hashCode() { public int hashCode() {
return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + (null == iface ? 0 : iface.hashCode());
(null == iface ? 0 : iface.hashCode());
} }
public boolean autoStarting() { public boolean isAutostart() {
return autostart; return autostart;
} }
@ -61,6 +62,7 @@ class RemoteObject {
return iface; return iface;
} }
@Override
public String toString() { public String toString() {
return busname + ":" + objectpath + ":" + iface; return busname + ":" + objectpath + ":" + iface;
} }

View file

@ -1,51 +1,106 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
class SignalTuple { import java.util.HashSet;
String type; import java.util.Set;
String name;
String object;
String source;
public SignalTuple(String type, String name, String object, String source) { /**
this.type = type; * @deprecated this class was used as map key internally and is no longer in use
this.name = name; */
this.object = object; @Deprecated(forRemoval = true, since = "4.2.0 - 2022-08-18")
this.source = source; public class SignalTuple {
private final String type;
private final String name;
private final String object;
private final String source;
public SignalTuple(String _type, String _name, String _object, String _source) {
this.type = _type;
this.name = _name;
this.object = _object;
this.source = _source;
} }
public boolean equals(Object o) { @Override
if (!(o instanceof SignalTuple)) return false; public boolean equals(Object _o) {
SignalTuple other = (SignalTuple) o; if (!(_o instanceof SignalTuple)) {
if (null == this.type && null != other.type) return false; return false;
if (null != this.type && !this.type.equals(other.type)) return false; }
if (null == this.name && null != other.name) return false; SignalTuple other = (SignalTuple) _o;
if (null != this.name && !this.name.equals(other.name)) return false; if (null == this.type && null != other.type) {
if (null == this.object && null != other.object) return false; return false;
if (null != this.object && !this.object.equals(other.object)) return false; } else if (null != this.type && !this.type.equals(other.type)) {
if (null == this.source && null != other.source) return false; return false;
if (null != this.source && !this.source.equals(other.source)) return false; } else if (null == this.name && null != other.name) {
return false;
} else if (null != this.name && !this.name.equals(other.name)) {
return false;
} else if (null == this.object && null != other.object) {
return false;
} else if (null != this.object && !this.object.equals(other.object)) {
return false;
} else if (null == this.source && null != other.source) {
return false;
} else if (null != this.source && !this.source.equals(other.source)) {
return false;
}
return true; return true;
} }
@Override
public int hashCode() { public int hashCode() {
return (null == type ? 0 : type.hashCode()) return (null == type ? 0 : type.hashCode()) + (null == name ? 0 : name.hashCode()) + (null == source ? 0 : source.hashCode()) + (null == object ? 0 : object.hashCode());
+ (null == name ? 0 : name.hashCode())
+ (null == source ? 0 : source.hashCode())
+ (null == object ? 0 : object.hashCode());
} }
@Override
public String toString() { public String toString() {
return "SignalTuple(" + type + "," + name + "," + object + "," + source + ")"; return "SignalTuple(" + type + "," + name + "," + object + "," + source + ")";
} }
}
/**
* Get a {@link Set} of all possible SignalTuples that we can have, given the 4 parameters.
* @param _type interface type
* @param _name name
* @param _object object
* @param _source source
* @return {@link Set} of {@link SignalTuple}, never null
* @deprecated should no longer be used
*/
@Deprecated
public static Set<SignalTuple> getAllPossibleTuples(String _type, String _name, String _object, String _source) {
Set<SignalTuple> allTuples = new HashSet<>();
// Tuple with no null
allTuples.add(new SignalTuple(_type, _name, _object, _source));
// Tuples with one null
allTuples.add(new SignalTuple(null, _name, _object, _source));
allTuples.add(new SignalTuple(_type, null, _object, _source));
allTuples.add(new SignalTuple(_type, _name, null, _source));
allTuples.add(new SignalTuple(_type, _name, _object, null));
// Tuples where type is null, and one other null
allTuples.add(new SignalTuple(null, null, _object, _source));
allTuples.add(new SignalTuple(null, _name, null, _source));
allTuples.add(new SignalTuple(null, _name, _object, null));
// Tuples where name is null, and one other null
allTuples.add(new SignalTuple(_type, null, null, _source));
allTuples.add(new SignalTuple(_type, null, _object, null));
// Tuples where object is null, and one other null
allTuples.add(new SignalTuple(null, _name, null, _source));
allTuples.add(new SignalTuple(_type, _name, null, null));
// Tuples where source is null, and one other null
allTuples.add(new SignalTuple(null, _name, _object, null));
allTuples.add(new SignalTuple(_type, _name, null, null));
// Tuples with three nulls
allTuples.add(new SignalTuple(_type, null, null, null));
allTuples.add(new SignalTuple(null, _name, null, null));
allTuples.add(new SignalTuple(null, null, _object, null));
allTuples.add(new SignalTuple(null, null, null, _source));
return allTuples;
}
}

View file

@ -1,42 +1,32 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/** /**
* An alternative to a WeakReference when you don't want * An alternative to a WeakReference when you don't want
* that behaviour. * that behavior.
*/ */
public class StrongReference<T> extends WeakReference<T> { public class StrongReference<T> extends WeakReference<T> {
T referant; private T referant;
public StrongReference(T referant) { public StrongReference(T _referant) {
super(referant); super(_referant);
this.referant = referant; this.referant = _referant;
} }
@Override
public void clear() { public void clear() {
referant = null; referant = null;
} }
@Override
public boolean enqueue() { public boolean enqueue() {
return false; return false;
} }
@Override
public T get() { public T get() {
return referant; return referant;
} }
public boolean isEnqueued() {
return false;
}
} }

View file

@ -1,13 +1,3 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
/** /**

View file

@ -0,0 +1,96 @@
package org.freedesktop.dbus;
import org.freedesktop.dbus.types.DBusStructType;
import org.freedesktop.dbus.types.Variant;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
/**
* Helper util to create {@link Struct} subclasses when receiving it from DBus.
*
* @author David M.
* @since v3.2.1 - 2019-10-25
*/
public final class StructHelper {
private StructHelper() {
}
/**
* Creates a instance of the given {@link Struct} subclass if the given variant is some sort of Struct.
* @param _variant variant to convert
* @param _structClass {@link Struct} subclass to create
*
* @param <T> type of struct
*
* @return instance of _structClass or null if _variant is not Struct compatible or any input parameter is null
*
* @throws NoSuchMethodException when no constructor can be found for the arguments of the struct
* @throws SecurityException when constructor cannot be accesses
* @throws InstantiationException when reflection fails
* @throws IllegalAccessException if this Constructor object is enforcing Java language access control and the underlying constructor is inaccessible.
* @throws IllegalArgumentException when data types are incompatible
* @throws InvocationTargetException if the underlying constructor throws an exception
*/
public static <T extends Struct> T createStructFromVariant(Variant<?> _variant, Class<T> _structClass)
throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (_variant == null || _structClass == null) {
return null;
}
if (_variant.getType() instanceof DBusStructType && _variant.getValue() instanceof Object[]) {
Class<?>[] argTypes = Arrays.stream((Object[]) _variant.getValue()).map(a -> a.getClass()).toArray(size -> new Class<?>[size]);
return createStruct(argTypes, _variant.getValue(), _structClass);
}
return null;
}
/**
* Will create a new {@link Struct} subclass instance if possible.
* May replace Wrapper-classes with primitive classes in _constructorArgs if constructor does not match.
*
* @param _constructorArgs argument-classes expected by constructor
* @param _values values passed to the constructor
* @param _classToConstruct {@link Struct} subclass to instantiate
*
* @param <T> type of struct
*
* @return instance of _classToConstruct or null if any input argument is null
*
* @throws NoSuchMethodException when no constructor can be found for the arguments of the struct
* @throws SecurityException when constructor cannot be accesses
* @throws InstantiationException when reflection fails
* @throws IllegalAccessException if this Constructor object is enforcing Java language access control and the underlying constructor is inaccessible.
* @throws IllegalArgumentException when data types are incompatible
* @throws InvocationTargetException if the underlying constructor throws an exception
*/
public static <T extends Struct> T createStruct(Class<?>[] _constructorArgs, Object _values, Class<T> _classToConstruct)
throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (_constructorArgs == null || _classToConstruct == null || _values == null) {
return null;
}
try {
Constructor<T> declaredConstructor = _classToConstruct.getDeclaredConstructor(_constructorArgs);
declaredConstructor.setAccessible(true);
if (_values instanceof Object[]) {
return declaredConstructor.newInstance((Object[]) _values);
} else {
return declaredConstructor.newInstance(_values);
}
} catch (NoSuchMethodException | SecurityException _ex) {
for (int i = 0; i < _constructorArgs.length; i++) {
Class<?> class1 = _constructorArgs[i];
if (ArrayFrob.getWrapperToPrimitiveTypes().containsKey(class1)) {
_constructorArgs[i] = ArrayFrob.getWrapperToPrimitiveTypes().get(class1);
return createStruct(_constructorArgs, _values, _classToConstruct);
}
}
}
throw new NoSuchMethodException("Cannot find suitable constructor for arguments " + Arrays.toString(_constructorArgs) + " in class " + _classToConstruct + ".");
}
}

View file

@ -1,835 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import cx.ath.matthew.unix.UnixSocket;
import cx.ath.matthew.unix.UnixSocketAddress;
import cx.ath.matthew.utils.Hexdump;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Random;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
public class Transport {
public static class SASL {
public static class Command {
private int command;
private int mechs;
private String data;
private String response;
public Command() {
}
public Command(String s) throws IOException {
String[] ss = s.split(" ");
if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating command from: " + Arrays.toString(ss));
if (0 == col.compare(ss[0], "OK")) {
command = COMMAND_OK;
data = ss[1];
} else if (0 == col.compare(ss[0], "AUTH")) {
command = COMMAND_AUTH;
if (ss.length > 1) {
if (0 == col.compare(ss[1], "EXTERNAL"))
mechs = AUTH_EXTERNAL;
else if (0 == col.compare(ss[1], "DBUS_COOKIE_SHA1"))
mechs = AUTH_SHA;
else if (0 == col.compare(ss[1], "ANONYMOUS"))
mechs = AUTH_ANON;
}
if (ss.length > 2)
data = ss[2];
} else if (0 == col.compare(ss[0], "DATA")) {
command = COMMAND_DATA;
data = ss[1];
} else if (0 == col.compare(ss[0], "REJECTED")) {
command = COMMAND_REJECTED;
for (int i = 1; i < ss.length; i++)
if (0 == col.compare(ss[i], "EXTERNAL"))
mechs |= AUTH_EXTERNAL;
else if (0 == col.compare(ss[i], "DBUS_COOKIE_SHA1"))
mechs |= AUTH_SHA;
else if (0 == col.compare(ss[i], "ANONYMOUS"))
mechs |= AUTH_ANON;
} else if (0 == col.compare(ss[0], "BEGIN")) {
command = COMMAND_BEGIN;
} else if (0 == col.compare(ss[0], "CANCEL")) {
command = COMMAND_CANCEL;
} else if (0 == col.compare(ss[0], "ERROR")) {
command = COMMAND_ERROR;
data = ss[1];
} else {
throw new IOException(getString("invalidCommand") + ss[0]);
}
if (Debug.debug) Debug.print(Debug.VERBOSE, "Created command: " + this);
}
public int getCommand() {
return command;
}
public int getMechs() {
return mechs;
}
public String getData() {
return data;
}
public String getResponse() {
return response;
}
public void setResponse(String s) {
response = s;
}
public String toString() {
return "Command(" + command + ", " + mechs + ", " + data + ", " + null + ")";
}
}
private static Collator col = Collator.getInstance();
static {
col.setDecomposition(Collator.FULL_DECOMPOSITION);
col.setStrength(Collator.PRIMARY);
}
public static final int LOCK_TIMEOUT = 1000;
public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5;
public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2);
public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5;
public static final int COOKIE_TIMEOUT = 240;
public static final String COOKIE_CONTEXT = "org_freedesktop_java";
private String findCookie(String context, String ID) throws IOException {
String homedir = System.getProperty("user.home");
File f = new File(homedir + "/.dbus-keyrings/" + context);
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
String s = null;
String cookie = null;
long now = System.currentTimeMillis() / 1000;
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long timestamp = Long.parseLong(line[1]);
if (line[0].equals(ID) && (!(timestamp < 0 ||
(now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
(now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp))) {
cookie = line[2];
break;
}
}
r.close();
return cookie;
}
private void addCookie(String context, String ID, long timestamp, String cookie) throws IOException {
String homedir = System.getProperty("user.home");
File keydir = new File(homedir + "/.dbus-keyrings/");
File cookiefile = new File(homedir + "/.dbus-keyrings/" + context);
File lock = new File(homedir + "/.dbus-keyrings/" + context + ".lock");
File temp = new File(homedir + "/.dbus-keyrings/" + context + ".temp");
// ensure directory exists
if (!keydir.exists()) keydir.mkdirs();
// acquire lock
long start = System.currentTimeMillis();
while (!lock.createNewFile() && LOCK_TIMEOUT > (System.currentTimeMillis() - start)) ;
// read old file
Vector<String> lines = new Vector<String>();
if (cookiefile.exists()) {
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile)));
String s = null;
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long time = Long.parseLong(line[1]);
// expire stale cookies
if ((timestamp - time) < COOKIE_TIMEOUT)
lines.add(s);
}
r.close();
}
// add cookie
lines.add(ID + " " + timestamp + " " + cookie);
// write temp file
PrintWriter w = new PrintWriter(new FileOutputStream(temp));
for (String l : lines)
w.println(l);
w.close();
// atomically move to old file
if (!temp.renameTo(cookiefile)) {
cookiefile.delete();
temp.renameTo(cookiefile);
}
// remove lock
lock.delete();
}
/**
* Takes the string, encodes it as hex and then turns it into a string again.
* No, I don't know why either.
*/
private String stupidlyEncode(String data) {
return Hexdump.toHex(data.getBytes()).replaceAll(" ", "");
}
private String stupidlyEncode(byte[] data) {
return Hexdump.toHex(data).replaceAll(" ", "");
}
private byte getNibble(char c) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (byte) (c - '0');
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return (byte) (c - 'A' + 10);
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return (byte) (c - 'a' + 10);
default:
return 0;
}
}
private String stupidlyDecode(String data) {
char[] cs = new char[data.length()];
char[] res = new char[cs.length / 2];
data.getChars(0, data.length(), cs, 0);
for (int i = 0, j = 0; j < res.length; i += 2, j++) {
int b = 0;
b |= getNibble(cs[i]) << 4;
b |= getNibble(cs[i + 1]);
res[j] = (char) b;
}
return new String(res);
}
public static final int MODE_SERVER = 1;
public static final int MODE_CLIENT = 2;
public static final int AUTH_NONE = 0;
public static final int AUTH_EXTERNAL = 1;
public static final int AUTH_SHA = 2;
public static final int AUTH_ANON = 4;
public static final int COMMAND_AUTH = 1;
public static final int COMMAND_DATA = 2;
public static final int COMMAND_REJECTED = 3;
public static final int COMMAND_OK = 4;
public static final int COMMAND_BEGIN = 5;
public static final int COMMAND_CANCEL = 6;
public static final int COMMAND_ERROR = 7;
public static final int INITIAL_STATE = 0;
public static final int WAIT_DATA = 1;
public static final int WAIT_OK = 2;
public static final int WAIT_REJECT = 3;
public static final int WAIT_AUTH = 4;
public static final int WAIT_BEGIN = 5;
public static final int AUTHENTICATED = 6;
public static final int FAILED = 7;
public static final int OK = 1;
public static final int CONTINUE = 2;
public static final int ERROR = 3;
public static final int REJECT = 4;
public Command receive(InputStream s) throws IOException {
StringBuffer sb = new StringBuffer();
top:
while (true) {
int c = s.read();
switch (c) {
case -1:
throw new IOException("Stream unexpectedly short (broken pipe)");
case 0:
case '\r':
continue;
case '\n':
break top;
default:
sb.append((char) c);
}
}
if (Debug.debug) Debug.print(Debug.VERBOSE, "received: " + sb);
try {
return new Command(sb.toString());
} catch (Exception e) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, e);
return new Command();
}
}
public void send(OutputStream out, int command, String... data) throws IOException {
StringBuffer sb = new StringBuffer();
switch (command) {
case COMMAND_AUTH:
sb.append("AUTH");
break;
case COMMAND_DATA:
sb.append("DATA");
break;
case COMMAND_REJECTED:
sb.append("REJECTED");
break;
case COMMAND_OK:
sb.append("OK");
break;
case COMMAND_BEGIN:
sb.append("BEGIN");
break;
case COMMAND_CANCEL:
sb.append("CANCEL");
break;
case COMMAND_ERROR:
sb.append("ERROR");
break;
default:
return;
}
for (String s : data) {
sb.append(' ');
sb.append(s);
}
sb.append('\r');
sb.append('\n');
if (Debug.debug) Debug.print(Debug.VERBOSE, "sending: " + sb);
out.write(sb.toString().getBytes());
}
public int do_challenge(int auth, Command c) throws IOException {
switch (auth) {
case AUTH_SHA:
String[] reply = stupidlyDecode(c.getData()).split(" ");
if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.toString(reply));
if (3 != reply.length) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Reply is not length 3");
return ERROR;
}
String context = reply[0];
String ID = reply[1];
String serverchallenge = reply[2];
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException NSAe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
return ERROR;
}
byte[] buf = new byte[8];
Message.marshallintBig(System.currentTimeMillis(), buf, 0, 8);
String clientchallenge = stupidlyEncode(md.digest(buf));
md.reset();
long start = System.currentTimeMillis();
String cookie = null;
while (null == cookie && (System.currentTimeMillis() - start) < LOCK_TIMEOUT)
cookie = findCookie(context, ID);
if (null == cookie) {
if (Debug.debug)
Debug.print(Debug.DEBUG, "Did not find a cookie in context " + context + " with ID " + ID);
return ERROR;
}
String response = serverchallenge + ":" + clientchallenge + ":" + cookie;
buf = md.digest(response.getBytes());
if (Debug.debug)
Debug.print(Debug.VERBOSE, "Response: " + response + " hash: " + Hexdump.format(buf));
response = stupidlyEncode(buf);
c.setResponse(stupidlyEncode(clientchallenge + " " + response));
return OK;
default:
if (Debug.debug) Debug.print(Debug.DEBUG, "Not DBUS_COOKIE_SHA1 authtype.");
return ERROR;
}
}
public String challenge = "";
public String cookie = "";
public int do_response(int auth, String Uid, String kernelUid, Command c) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException NSAe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
return ERROR;
}
switch (auth) {
case AUTH_NONE:
switch (c.getMechs()) {
case AUTH_ANON:
return OK;
case AUTH_EXTERNAL:
if (0 == col.compare(Uid, c.getData()) &&
(null == kernelUid || 0 == col.compare(Uid, kernelUid)))
return OK;
else
return ERROR;
case AUTH_SHA:
String context = COOKIE_CONTEXT;
long id = System.currentTimeMillis();
byte[] buf = new byte[8];
Message.marshallintBig(id, buf, 0, 8);
challenge = stupidlyEncode(md.digest(buf));
Random r = new Random();
r.nextBytes(buf);
cookie = stupidlyEncode(md.digest(buf));
try {
addCookie(context, "" + id, id / 1000, cookie);
} catch (IOException IOe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe);
}
if (Debug.debug)
Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge);
c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge));
return CONTINUE;
default:
return ERROR;
}
case AUTH_SHA:
String[] response = stupidlyDecode(c.getData()).split(" ");
if (response.length < 2) return ERROR;
String cchal = response[0];
String hash = response[1];
String prehash = challenge + ":" + cchal + ":" + cookie;
byte[] buf = md.digest(prehash.getBytes());
String posthash = stupidlyEncode(buf);
if (Debug.debug)
Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash);
if (0 == col.compare(posthash, hash))
return OK;
else
return ERROR;
default:
return ERROR;
}
}
public String[] getTypes(int types) {
switch (types) {
case AUTH_EXTERNAL:
return new String[]{"EXTERNAL"};
case AUTH_SHA:
return new String[]{"DBUS_COOKIE_SHA1"};
case AUTH_ANON:
return new String[]{"ANONYMOUS"};
case AUTH_SHA + AUTH_EXTERNAL:
return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"};
case AUTH_SHA + AUTH_ANON:
return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"};
case AUTH_EXTERNAL + AUTH_ANON:
return new String[]{"ANONYMOUS", "EXTERNAL"};
case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"};
default:
return new String[]{};
}
}
/**
* performs SASL auth on the given streams.
* Mode selects whether to run as a SASL server or client.
* Types is a bitmask of the available auth types.
* Returns true if the auth was successful and false if it failed.
*/
@SuppressWarnings("unchecked")
public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException {
String username = System.getProperty("user.name");
String Uid = null;
String kernelUid = null;
try {
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
Method m = c.getMethod("getUid");
Object o = c.newInstance();
long uid = (Long) m.invoke(o);
Uid = stupidlyEncode("" + uid);
} catch (Exception e) {
Uid = stupidlyEncode(username);
}
Command c;
int failed = 0;
int current = 0;
int state = INITIAL_STATE;
while (state != AUTHENTICATED && state != FAILED) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH state: " + state);
switch (mode) {
case MODE_CLIENT:
switch (state) {
case INITIAL_STATE:
if (null == us)
out.write(new byte[]{0});
else
us.sendCredentialByte((byte) 0);
send(out, COMMAND_AUTH);
state = WAIT_DATA;
break;
case WAIT_DATA:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_DATA:
switch (do_challenge(current, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
break;
case OK:
send(out, COMMAND_DATA, c.getResponse());
state = WAIT_OK;
break;
case ERROR:
send(out, COMMAND_ERROR, c.getResponse());
break;
}
break;
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
case COMMAND_ERROR:
send(out, COMMAND_CANCEL);
state = WAIT_REJECT;
break;
case COMMAND_OK:
send(out, COMMAND_BEGIN);
state = AUTHENTICATED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_OK:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_OK:
send(out, COMMAND_BEGIN);
state = AUTHENTICATED;
break;
case COMMAND_ERROR:
case COMMAND_DATA:
send(out, COMMAND_CANCEL);
state = WAIT_REJECT;
break;
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
state = WAIT_DATA;
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_REJECT:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
default:
state = FAILED;
break;
}
break;
default:
state = FAILED;
}
break;
case MODE_SERVER:
switch (state) {
case INITIAL_STATE:
byte[] buf = new byte[1];
if (null == us) {
in.read(buf);
} else {
buf[0] = us.recvCredentialByte();
int kuid = us.getPeerUID();
if (kuid >= 0)
kernelUid = stupidlyEncode("" + kuid);
}
if (0 != buf[0]) state = FAILED;
else state = WAIT_AUTH;
break;
case WAIT_AUTH:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_AUTH:
if (null == c.getData()) {
send(out, COMMAND_REJECTED, getTypes(types));
} else {
switch (do_response(current, Uid, kernelUid, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
current = c.getMechs();
state = WAIT_DATA;
break;
case OK:
send(out, COMMAND_OK, guid);
state = WAIT_BEGIN;
current = 0;
break;
case REJECT:
send(out, COMMAND_REJECTED, getTypes(types));
current = 0;
break;
}
}
break;
case COMMAND_ERROR:
send(out, COMMAND_REJECTED, getTypes(types));
break;
case COMMAND_BEGIN:
state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_DATA:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_DATA:
switch (do_response(current, Uid, kernelUid, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
state = WAIT_DATA;
break;
case OK:
send(out, COMMAND_OK, guid);
state = WAIT_BEGIN;
current = 0;
break;
case REJECT:
send(out, COMMAND_REJECTED, getTypes(types));
current = 0;
break;
}
break;
case COMMAND_ERROR:
case COMMAND_CANCEL:
send(out, COMMAND_REJECTED, getTypes(types));
state = WAIT_AUTH;
break;
case COMMAND_BEGIN:
state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_BEGIN:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_ERROR:
case COMMAND_CANCEL:
send(out, COMMAND_REJECTED, getTypes(types));
state = WAIT_AUTH;
break;
case COMMAND_BEGIN:
state = AUTHENTICATED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
default:
state = FAILED;
}
break;
default:
return false;
}
}
return state == AUTHENTICATED;
}
}
public MessageReader min;
public MessageWriter mout;
public Transport() {
}
public static String genGUID() {
Random r = new Random();
byte[] buf = new byte[16];
r.nextBytes(buf);
String guid = Hexdump.toHex(buf);
return guid.replaceAll(" ", "");
}
public Transport(BusAddress address) throws IOException {
connect(address);
}
public Transport(String address) throws IOException, ParseException {
connect(new BusAddress(address));
}
public Transport(String address, int timeout) throws IOException, ParseException {
connect(new BusAddress(address), timeout);
}
public void connect(String address) throws IOException, ParseException {
connect(new BusAddress(address), 0);
}
public void connect(String address, int timeout) throws IOException, ParseException {
connect(new BusAddress(address), timeout);
}
public void connect(BusAddress address) throws IOException {
connect(address, 0);
}
public void connect(BusAddress address, int timeout) throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Connecting to " + address);
OutputStream out = null;
InputStream in = null;
UnixSocket us = null;
Socket s = null;
int mode = 0;
int types = 0;
if ("unix".equals(address.getType())) {
types = SASL.AUTH_EXTERNAL;
mode = SASL.MODE_CLIENT;
us = new UnixSocket();
if (null != address.getParameter("abstract"))
us.connect(new UnixSocketAddress(address.getParameter("abstract"), true));
else if (null != address.getParameter("path"))
us.connect(new UnixSocketAddress(address.getParameter("path"), false));
us.setPassCred(true);
in = us.getInputStream();
out = us.getOutputStream();
} else if ("tcp".equals(address.getType())) {
types = SASL.AUTH_SHA;
if (null != address.getParameter("listen")) {
mode = SASL.MODE_SERVER;
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
s = ss.accept();
} else {
mode = SASL.MODE_CLIENT;
s = new Socket();
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
}
in = s.getInputStream();
out = s.getOutputStream();
} else {
throw new IOException(getString("unknownAddress") + address.getType());
}
if (!(new SASL()).auth(mode, types, address.getParameter("guid"), out, in, us)) {
out.close();
throw new IOException(getString("errorAuth"));
}
if (null != us) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket");
if (timeout == 1)
us.setBlocking(false);
else
us.setSoTimeout(timeout);
}
if (null != s) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket");
s.setSoTimeout(timeout);
}
mout = new MessageWriter(out);
min = new MessageReader(in);
}
public void disconnect() throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Transport");
min.close();
mout.close();
}
}

View file

@ -1,19 +1,11 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
/** /**
* This class should be extended to create Tuples. * This class should be extended to create Tuples.
*
* Any such class may be used as the return type for a method * Any such class may be used as the return type for a method
* which returns multiple values. * which returns multiple values.
*
* All fields in the Tuple which you wish to be serialized and sent to the * All fields in the Tuple which you wish to be serialized and sent to the
* remote method should be annotated with the org.freedesktop.dbus.Position * remote method should be annotated with the org.freedesktop.dbus.Position
* annotation, in the order they should appear to DBus. * annotation, in the order they should appear to DBus.

View file

@ -0,0 +1,15 @@
package org.freedesktop.dbus;
/**
* Interface for retrieving generic type information with reflection.
* <p>
* Examples:
* <pre>{@code
* public interface ListOfStrings extends TypeRef<List<String>> {
* }
* } </pre>
*
* @param <T> generic type
*/
public interface TypeRef<T> {
}

View file

@ -1,13 +1,3 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus; package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
@ -15,18 +5,20 @@ import org.freedesktop.dbus.exceptions.DBusException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class TypeSignature { public class TypeSignature {
// CHECKSTYLE:OFF
String sig; String sig;
// CHECKSTYLE:ON
public TypeSignature(String sig) { public TypeSignature(String _sig) {
this.sig = sig; this.sig = _sig;
} }
public TypeSignature(Type[] types) throws DBusException { public TypeSignature(Type[] _types) throws DBusException {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
for (Type t : types) { for (Type t : _types) {
String[] ts = Marshalling.getDBusType(t); String[] ts = Marshalling.getDBusType(t);
for (String s : ts) for (String s : ts) {
sb.append(s); sb.append(s);
}
} }
this.sig = sb.toString(); this.sig = sb.toString();
} }

View file

@ -1,122 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.text.MessageFormat;
import static org.freedesktop.dbus.Gettext.getString;
/**
* Class to represent 16-bit unsigned integers.
*/
@SuppressWarnings("serial")
public class UInt16 extends Number implements Comparable<UInt16> {
/**
* Maximum possible value.
*/
public static final int MAX_VALUE = 65535;
/**
* Minimum possible value.
*/
public static final int MIN_VALUE = 0;
private int value;
/**
* Create a UInt16 from an int.
*
* @param value Must be within MIN_VALUE&ndash;MAX_VALUE
* @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE
*/
public UInt16(int value) {
if (value < MIN_VALUE || value > MAX_VALUE)
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE}));
this.value = value;
}
/**
* Create a UInt16 from a String.
*
* @param value Must parse to a valid integer within MIN_VALUE&ndash;MAX_VALUE
* @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE
*/
public UInt16(String value) {
this(Integer.parseInt(value));
}
/**
* The value of this as a byte.
*/
public byte byteValue() {
return (byte) value;
}
/**
* The value of this as a double.
*/
public double doubleValue() {
return (double) value;
}
/**
* The value of this as a float.
*/
public float floatValue() {
return (float) value;
}
/**
* The value of this as a int.
*/
public int intValue() {
return /*(int)*/ value;
}
/**
* The value of this as a long.
*/
public long longValue() {
return (long) value;
}
/**
* The value of this as a short.
*/
public short shortValue() {
return (short) value;
}
/**
* Test two UInt16s for equality.
*/
public boolean equals(Object o) {
return o instanceof UInt16 && ((UInt16) o).value == this.value;
}
public int hashCode() {
return /*(int)*/ value;
}
/**
* Compare two UInt16s.
*
* @return 0 if equal, -ve or +ve if they are different.
*/
public int compareTo(UInt16 other) {
return /*(int)*/ (this.value - other.value);
}
/**
* The value of this as a string.
*/
public String toString() {
return "" + value;
}
}

View file

@ -1,122 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.text.MessageFormat;
import static org.freedesktop.dbus.Gettext.getString;
/**
* Class to represent unsigned 32-bit numbers.
*/
@SuppressWarnings("serial")
public class UInt32 extends Number implements Comparable<UInt32> {
/**
* Maximum allowed value
*/
public static final long MAX_VALUE = 4294967295L;
/**
* Minimum allowed value
*/
public static final long MIN_VALUE = 0;
private long value;
/**
* Create a UInt32 from a long.
*
* @param value Must be a valid integer within MIN_VALUE&ndash;MAX_VALUE
* @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE
*/
public UInt32(long value) {
if (value < MIN_VALUE || value > MAX_VALUE)
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE}));
this.value = value;
}
/**
* Create a UInt32 from a String.
*
* @param value Must parse to a valid integer within MIN_VALUE&ndash;MAX_VALUE
* @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE
*/
public UInt32(String value) {
this(Long.parseLong(value));
}
/**
* The value of this as a byte.
*/
public byte byteValue() {
return (byte) value;
}
/**
* The value of this as a double.
*/
public double doubleValue() {
return (double) value;
}
/**
* The value of this as a float.
*/
public float floatValue() {
return (float) value;
}
/**
* The value of this as a int.
*/
public int intValue() {
return (int) value;
}
/**
* The value of this as a long.
*/
public long longValue() {
return /*(long)*/ value;
}
/**
* The value of this as a short.
*/
public short shortValue() {
return (short) value;
}
/**
* Test two UInt32s for equality.
*/
public boolean equals(Object o) {
return o instanceof UInt32 && ((UInt32) o).value == this.value;
}
public int hashCode() {
return (int) value;
}
/**
* Compare two UInt32s.
*
* @return 0 if equal, -ve or +ve if they are different.
*/
public int compareTo(UInt32 other) {
return (int) (this.value - other.value);
}
/**
* The value of this as a string
*/
public String toString() {
return "" + value;
}
}

View file

@ -1,203 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import java.math.BigInteger;
import java.text.MessageFormat;
import static org.freedesktop.dbus.Gettext.getString;
/**
* Class to represent unsigned 64-bit numbers.
* Warning: Any functions which take or return a <tt>long</tt>
* are restricted to the range of a signed 64bit number.
* Use the BigInteger methods if you wish access to the full
* range.
*/
@SuppressWarnings("serial")
public class UInt64 extends Number implements Comparable<UInt64> {
/**
* Maximum allowed value (when accessed as a long)
*/
public static final long MAX_LONG_VALUE = Long.MAX_VALUE;
/**
* Maximum allowed value (when accessed as a BigInteger)
*/
public static final BigInteger MAX_BIG_VALUE = new BigInteger("18446744073709551615");
/**
* Minimum allowed value
*/
public static final long MIN_VALUE = 0;
private BigInteger value;
private long top;
private long bottom;
/**
* Create a UInt64 from a long.
*
* @param value Must be a valid integer within MIN_VALUE&ndash;MAX_VALUE
* @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE
*/
public UInt64(long value) {
if (value < MIN_VALUE || value > MAX_LONG_VALUE)
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_LONG_VALUE}));
this.value = new BigInteger("" + value);
this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue();
this.bottom = this.value.and(new BigInteger("4294967295")).longValue();
}
/**
* Create a UInt64 from two longs.
*
* @param top Most significant 4 bytes.
* @param bottom Least significant 4 bytes.
*/
public UInt64(long top, long bottom) {
BigInteger a = new BigInteger("" + top);
a = a.shiftLeft(32);
a = a.add(new BigInteger("" + bottom));
if (0 > a.compareTo(BigInteger.ZERO))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE}));
if (0 < a.compareTo(MAX_BIG_VALUE))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE}));
this.value = a;
this.top = top;
this.bottom = bottom;
}
/**
* Create a UInt64 from a BigInteger
*
* @param value Must be a valid BigInteger between MIN_VALUE&ndash;MAX_BIG_VALUE
* @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE
*/
public UInt64(BigInteger value) {
if (null == value)
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
if (0 > value.compareTo(BigInteger.ZERO))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
if (0 < value.compareTo(MAX_BIG_VALUE))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
this.value = value;
this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue();
this.bottom = this.value.and(new BigInteger("4294967295")).longValue();
}
/**
* Create a UInt64 from a String.
*
* @param value Must parse to a valid integer within MIN_VALUE&ndash;MAX_BIG_VALUE
* @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE
*/
public UInt64(String value) {
if (null == value)
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
BigInteger a = new BigInteger(value);
if (0 > a.compareTo(BigInteger.ZERO))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
if (0 < a.compareTo(MAX_BIG_VALUE))
throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE}));
this.value = a;
this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue();
this.bottom = this.value.and(new BigInteger("4294967295")).longValue();
}
/**
* The value of this as a BigInteger.
*/
public BigInteger value() {
return value;
}
/**
* The value of this as a byte.
*/
public byte byteValue() {
return value.byteValue();
}
/**
* The value of this as a double.
*/
public double doubleValue() {
return value.doubleValue();
}
/**
* The value of this as a float.
*/
public float floatValue() {
return value.floatValue();
}
/**
* The value of this as a int.
*/
public int intValue() {
return value.intValue();
}
/**
* The value of this as a long.
*/
public long longValue() {
return value.longValue();
}
/**
* The value of this as a short.
*/
public short shortValue() {
return value.shortValue();
}
/**
* Test two UInt64s for equality.
*/
public boolean equals(Object o) {
return o instanceof UInt64 && this.value.equals(((UInt64) o).value);
}
public int hashCode() {
return value.hashCode();
}
/**
* Compare two UInt32s.
*
* @return 0 if equal, -ve or +ve if they are different.
*/
public int compareTo(UInt64 other) {
return this.value.compareTo(other.value);
}
/**
* The value of this as a string.
*/
public String toString() {
return value.toString();
}
/**
* Most significant 4 bytes.
*/
public long top() {
return top;
}
/**
* Least significant 4 bytes.
*/
public long bottom() {
return bottom;
}
}

View file

@ -1,136 +0,0 @@
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import org.freedesktop.dbus.exceptions.DBusException;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
/**
* A Wrapper class for Variant values.
* A method on DBus can send or receive a Variant.
* This will wrap another value whose type is determined at runtime.
* The Variant may be parameterized to restrict the types it may accept.
*/
public class Variant<T> {
private final T o;
private final Type type;
private final String sig;
/**
* Create a Variant from a basic type object.
*
* @param o The wrapped value.
* @throws IllegalArugmentException If you try and wrap Null or an object of a non-basic type.
*/
public Variant(T o) throws IllegalArgumentException {
if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant"));
type = o.getClass();
try {
String[] ss = Marshalling.getDBusType(o.getClass(), true);
if (ss.length != 1)
throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type);
this.sig = ss[0];
} catch (DBusException DBe) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{o.getClass(), DBe.getMessage()}));
}
this.o = o;
}
/**
* Create a Variant.
*
* @param o The wrapped value.
* @param type The explicit type of the value.
* @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus.
*/
public Variant(T o, Type type) throws IllegalArgumentException {
if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant"));
this.type = type;
try {
String[] ss = Marshalling.getDBusType(type);
if (ss.length != 1)
throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type);
this.sig = ss[0];
} catch (DBusException DBe) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{type, DBe.getMessage()}));
}
this.o = o;
}
/**
* Create a Variant.
*
* @param o The wrapped value.
* @param sig The explicit type of the value, as a dbus type string.
* @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus.
*/
public Variant(T o, String sig) throws IllegalArgumentException {
if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant"));
this.sig = sig;
try {
Vector<Type> ts = new Vector<Type>();
Marshalling.getJavaType(sig, ts, 1);
if (ts.size() != 1)
throw new IllegalArgumentException(getString("cannotWrapNoTypesInVariant") + sig);
this.type = ts.get(0);
} catch (DBusException DBe) {
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{sig, DBe.getMessage()}));
}
this.o = o;
}
/**
* Return the wrapped value.
*/
public T getValue() {
return o;
}
/**
* Return the type of the wrapped value.
*/
public Type getType() {
return type;
}
/**
* Return the dbus signature of the wrapped value.
*/
public String getSig() {
return sig;
}
/**
* Format the Variant as a string.
*/
public String toString() {
return "[" + o + "]";
}
/**
* Compare this Variant with another by comparing contents
*/
@SuppressWarnings("unchecked")
public boolean equals(Object other) {
if (null == other) return false;
if (!(other instanceof Variant)) return false;
return this.o.equals(((Variant<? extends Object>) other).o);
}
}

View file

@ -0,0 +1,28 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Mark an exported method as ignored.<br>
* It will not be included in introspection data, and it will not be
* remotely callable. This is only useful for a local DBus object, it has no meaning to remote objects.
* <p>
* Usage:
* </p>
* <pre>
* {@literal @}DBusInterfaceName("com.example.Bar")
* public interface Bar extends DBusInterface {
*
* {@literal @}DBusIgnore
* public void doSomethingInternal() {
* }
* }
* </pre>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBusIgnore {
}

View file

@ -0,0 +1,18 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Force the interface name to be different to the Java class name.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DBusInterfaceName {
/** The replacement interface name.
* @return value
*/
String value();
}

View file

@ -0,0 +1,20 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Force the member (method/signal) name on the bus to be different to the Java name.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE, ElementType.METHOD
})
public @interface DBusMemberName {
/** The replacement member name.
* @return value
*/
String value();
}

View file

@ -0,0 +1,24 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Container for the multiple {@link DBusProperty} annotations in the single class.
* You probably don't want to use this annotation in your code, please use {@link DBusProperty}.
*
* @see DBusProperty
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBusProperties {
/**
* Container for multiple properties
*
* @return value
*/
DBusProperty[] value();
}

View file

@ -0,0 +1,74 @@
package org.freedesktop.dbus.annotations;
import org.freedesktop.dbus.types.Variant;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Appends information about properties in the interface. The annotated properties are added to the introspection data.
* In case of complex type of the property please use {@link org.freedesktop.dbus.TypeRef}.
* <p>
* Usage:
* </p>
* <pre>
* {@literal @}DBusInterfaceName("com.example.Bar")
* {@literal @}DBusProperty(name = "Name", type = String.class)
* {@literal @}DBusProperty(name = "ListOfVariables", type = List.class, access = Access.READ)
* {@literal @}DBusProperty(name = "MapOfStringList", type = ComplexTypeWithMapAndList.class, access = Access.READ)
* public interface Bar extends DBusInterface {
*
* // TypeRef allows to provide detailed information about type
* interface ComplexTypeWithMapAndList extends TypeRef&lt;Map&lt;String, List&lt;String&gt;&gt;&gt; {
* }
* }
* </pre>
*
* @see org.freedesktop.dbus.interfaces.DBusInterface
* @see org.freedesktop.dbus.TypeRef
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DBusProperties.class)
public @interface DBusProperty {
/**
* Property name
*
* @return name
*/
String name();
/**
* type of the property, in case of complex types please create custom interface that extends {@link org.freedesktop.dbus.TypeRef}
*
* @return type
*/
Class<?> type() default Variant.class;
/**
* Property access type
*
* @return access
*/
Access access() default Access.READ_WRITE;
enum Access {
READ("read"),
READ_WRITE("readwrite"),
WRITE("write");
private final String accessName;
Access(String _accessName) {
this.accessName = _accessName;
}
public String getAccessName() {
return accessName;
}
}
}

View file

@ -0,0 +1,19 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Indicates that a DBus interface or method is deprecated
*/
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.Deprecated")
public @interface DeprecatedOnDBus {
/**
* Annotation value, true by default
*
* @return true when the annotated element deprecated
*/
boolean value() default true;
}

View file

@ -0,0 +1,16 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Define a C symbol to map to this method. Used by GLib only
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.GLib.CSymbol")
public @interface GlibCSymbol {
String value();
}

View file

@ -0,0 +1,13 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Description of the interface or method, returned in the introspection data
*/
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.Description")
public @interface IntrospectionDescription {
String value();
}

View file

@ -0,0 +1,16 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Give an error that the method can return
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.Method.Error")
public @interface MethodError {
String value();
}

View file

@ -0,0 +1,22 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Methods annotated with this do not send a reply
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.Method.NoReply")
public @interface MethodNoReply {
/**
* Annotation value, true by default
*
* @return true when a method doesn't send a reply
*/
boolean value() default true;
}

View file

@ -0,0 +1,19 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Position annotation, to annotate Struct fields
* to be sent over DBus.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Position {
/** The order of this field in the Struct.
* @return value
*/
int value();
}

View file

@ -0,0 +1,40 @@
package org.freedesktop.dbus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <b>From <a href="https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces">DBUS Specification</a>:</b><br>
* If set to false, the org.freedesktop.DBus.Properties.PropertiesChanged signal,<br>
* see the section called org.freedesktop.DBus.Properties is not guaranteed to be emitted if the property changes.<br>
* <br>
* If set to const the property never changes value during the lifetime of the object it belongs to, <br>
* and hence the signal is never emitted for it. <br>
* <br>
* If set to invalidates the signal is emitted but the value is not included in the signal.<br>
* <br>
* If set to true the signal is emitted with the value included. <br>
* The value for the annotation defaults to true if the enclosing interface element does not specify the annotation.
* Otherwise it defaults to the value specified in the enclosing interface element.<br>
* <br>
* This annotation is intended to be used by code generators to implement client-side caching of property values. <br>
* For all properties for which the annotation is set to const, invalidates or true the client may unconditionally <br>
* cache the values as the properties don't change or notifications are generated for them if they do.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DBusInterfaceName("org.freedesktop.DBus.Property.EmitsChangedSignal")
public @interface PropertiesEmitsChangedSignal {
EmitChangeSignal value();
enum EmitChangeSignal {
TRUE, INVALIDATES, CONST, FALSE;
@Override
public String toString() {
return name().toLowerCase();
}
}
}

View file

@ -0,0 +1,875 @@
package org.freedesktop.dbus.bin;
import org.freedesktop.dbus.Marshalling;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode;
import org.freedesktop.dbus.connections.transports.TransportConnection;
import org.freedesktop.dbus.errors.AccessDenied;
import org.freedesktop.dbus.errors.Error;
import org.freedesktop.dbus.errors.MatchRuleInvalid;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.interfaces.DBus;
import org.freedesktop.dbus.interfaces.DBus.NameOwnerChanged;
import org.freedesktop.dbus.interfaces.FatalException;
import org.freedesktop.dbus.interfaces.Introspectable;
import org.freedesktop.dbus.interfaces.Peer;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.messages.MethodReturn;
import org.freedesktop.dbus.types.UInt32;
import org.freedesktop.dbus.types.Variant;
import org.freedesktop.dbus.utils.Hexdump;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A replacement DBusDaemon
*/
public class DBusDaemon extends Thread implements Closeable {
public static final int QUEUE_POLL_WAIT = 500;
private static final Logger LOGGER =
LoggerFactory.getLogger(DBusDaemon.class);
private final Map<ConnectionStruct, DBusDaemonReaderThread> conns =
new ConcurrentHashMap<>();
private final Map<String, ConnectionStruct> names =
Collections.synchronizedMap(new HashMap<>()); // required because of "null" key
private final BlockingDeque<Pair<Message, WeakReference<ConnectionStruct>>> outqueue =
new LinkedBlockingDeque<>();
private final BlockingDeque<Pair<Message, WeakReference<ConnectionStruct>>> inqueue =
new LinkedBlockingDeque<>();
private final List<ConnectionStruct> sigrecips = new ArrayList<>();
private final DBusServer dbusServer = new DBusServer();
private final DBusDaemonSenderThread sender =
new DBusDaemonSenderThread();
private final AtomicBoolean run =
new AtomicBoolean(false);
private final AtomicInteger nextUnique = new AtomicInteger(0);
private final AbstractTransport transport;
public DBusDaemon(AbstractTransport _transport) {
setName(getClass().getSimpleName() + "-Thread");
transport = _transport;
names.put("org.freedesktop.DBus", null);
}
private void send(ConnectionStruct _connStruct, Message _msg) {
send(_connStruct, _msg, false);
}
private void send(ConnectionStruct _connStruct, Message _msg, boolean _head) {
// send to all connections
if (null == _connStruct) {
LOGGER.trace("Queuing message {} for all connections", _msg);
synchronized (conns) {
for (ConnectionStruct d : conns.keySet()) {
if (_head) {
outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(d)));
} else {
outqueue.addLast(new Pair<>(_msg, new WeakReference<>(d)));
}
}
}
} else {
LOGGER.trace("Queuing message {} for {}", _msg, _connStruct.unique);
if (_head) {
outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(_connStruct)));
} else {
outqueue.addLast(new Pair<>(_msg, new WeakReference<>(_connStruct)));
}
}
}
@Override
public void run() {
run.set(true);
sender.start();
while (isRunning()) {
try {
Pair<Message, WeakReference<ConnectionStruct>> pollFirst = inqueue.take();
ConnectionStruct connectionStruct = pollFirst.second.get();
if (connectionStruct != null) {
Message m = pollFirst.first;
logMessage("<inqueue> Got message {} from {}", m, connectionStruct.unique);
// check if they have hello'd
if (null == connectionStruct.unique && (!(m instanceof MethodCall) || !"org.freedesktop.DBus".equals(m.getDestination()) || !"Hello".equals(m.getName()))) {
send(connectionStruct, new Error("org.freedesktop.DBus", null, "org.freedesktop.DBus.Error.AccessDenied", m.getSerial(), "s", "You must send a Hello message"));
} else {
try {
if (null != connectionStruct.unique) {
m.setSource(connectionStruct.unique);
LOGGER.trace("Updated source to {}", connectionStruct.unique);
}
} catch (DBusException _ex) {
LOGGER.debug("Error setting source", _ex);
send(connectionStruct, new Error("org.freedesktop.DBus", null, "org.freedesktop.DBus.Error.GeneralError", m.getSerial(), "s", "Sending message failed"));
}
if ("org.freedesktop.DBus".equals(m.getDestination())) {
dbusServer.handleMessage(connectionStruct, pollFirst.first);
} else {
if (m instanceof DBusSignal) {
List<ConnectionStruct> l;
synchronized (sigrecips) {
l = new ArrayList<>(sigrecips);
}
List<ConnectionStruct> list = l;
for (ConnectionStruct d : list) {
send(d, m);
}
} else {
ConnectionStruct dest = names.get(m.getDestination());
if (null == dest) {
send(connectionStruct, new Error("org.freedesktop.DBus", null,
"org.freedesktop.DBus.Error.ServiceUnknown", m.getSerial(), "s",
String.format("The name `%s' does not exist", m.getDestination())));
} else {
send(dest, m);
}
}
}
}
}
} catch (DBusException _ex) {
LOGGER.debug("Error processing connection", _ex);
} catch (InterruptedException _ex) {
LOGGER.debug("Interrupted");
close();
interrupt();
}
}
}
private static void logMessage(String _logStr, Message _m, String _connUniqueId) {
Object logMsg = _m;
if (_m != null && Introspectable.class.getName().equals(_m.getInterface()) && !LOGGER.isTraceEnabled()) {
logMsg = "<Introspection data only visible in loglevel trace>";
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(_logStr, logMsg, _connUniqueId);
} else {
LOGGER.debug(_logStr, _m, _connUniqueId);
}
}
public synchronized boolean isRunning() {
return run.get();
}
@Override
public void close() {
run.set(false);
if (!conns.isEmpty()) {
// disconnect all remaining connection
Set<ConnectionStruct> connections = new HashSet<>(conns.keySet());
for (ConnectionStruct c : connections) {
removeConnection(c);
}
}
sender.terminate();
if (transport != null && transport.isConnected()) {
LOGGER.debug("Terminating transport {}", transport);
try {
// shutdown listener
transport.close();
} catch (IOException _ex) {
LOGGER.debug("Error closing transport", _ex);
}
}
}
private void removeConnection(ConnectionStruct _c) {
boolean exists = false;
synchronized (conns) {
if (conns.containsKey(_c)) {
DBusDaemonReaderThread r = conns.get(_c);
exists = true;
r.terminate();
conns.remove(_c);
}
}
if (exists) {
try {
if (null != _c.connection) {
_c.connection.close();
}
} catch (IOException _exIo) {
LOGGER.trace("Error while closing socketchannel", _exIo);
}
synchronized (names) {
List<String> toRemove = new ArrayList<>();
for (String name : names.keySet()) {
if (names.get(name) == _c) {
toRemove.add(name);
try {
send(null, new NameOwnerChanged("/org/freedesktop/DBus", name, _c.unique, ""));
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
}
}
}
for (String name : toRemove) {
names.remove(name);
}
}
}
}
void addSock(TransportConnection _s) throws IOException {
LOGGER.debug("New Client");
ConnectionStruct c = new ConnectionStruct(_s);
DBusDaemonReaderThread r = new DBusDaemonReaderThread(c);
conns.put(c, r);
r.start();
}
public static void syntax() {
System.out.println("Syntax: DBusDaemon [--version] [-v] [--help] [-h] [--listen address] "
+ "[-l address] [--print-address] [-r] [--pidfile file] [-p file] [--addressfile file] "
+ "[--auth-mode AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL] [-m AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL]"
+ "[-a file] [--unix] [-u] [--tcp] [-t] ");
System.exit(1);
}
public static void version() {
System.out.println("D-Bus Java Version: " + System.getProperty("Version"));
System.exit(1);
}
public static void saveFile(String _data, String _file) throws IOException {
try (PrintWriter w = new PrintWriter(new FileOutputStream(_file))) {
w.println(_data);
}
}
public static void main(String[] _args) throws Exception {
String addr = null;
String pidfile = null;
String addrfile = null;
String authModeStr = null;
boolean printaddress = false;
boolean unix = true;
boolean tcp = false;
// parse options
try {
for (int i = 0; i < _args.length; i++) {
if ("--help".equals(_args[i]) || "-h".equals(_args[i])) {
syntax();
} else if ("--version".equals(_args[i]) || "-v".equals(_args[i])) {
version();
} else if ("--listen".equals(_args[i]) || "-l".equals(_args[i])) {
addr = _args[++i];
} else if ("--pidfile".equals(_args[i]) || "-p".equals(_args[i])) {
pidfile = _args[++i];
} else if ("--addressfile".equals(_args[i]) || "-a".equals(_args[i])) {
addrfile = _args[++i];
} else if ("--print-address".equals(_args[i]) || "-r".equals(_args[i])) {
printaddress = true;
} else if ("--unix".equals(_args[i]) || "-u".equals(_args[i])) {
unix = true;
tcp = false;
} else if ("--tcp".equals(_args[i]) || "-t".equals(_args[i])) {
tcp = true;
unix = false;
} else if ("--auth-mode".equals(_args[i]) || "-m".equals(_args[i])) {
authModeStr = _args[++i];
} else {
syntax();
}
}
} catch (ArrayIndexOutOfBoundsException _ex) {
syntax();
}
// generate a random address if none specified
if (null == addr && unix) {
addr = TransportBuilder.createDynamicSession("UNIX", true);
} else if (null == addr && tcp) {
addr = TransportBuilder.createDynamicSession("TCP", true);
}
BusAddress address = BusAddress.of(addr);
// print address to stdout
if (printaddress) {
System.out.println(addr);
}
SaslAuthMode saslAuthMode = null;
if (authModeStr != null) {
String selectedMode = authModeStr;
saslAuthMode = Arrays.stream(SaslAuthMode.values())
.filter(e -> e.name().toLowerCase().matches(selectedMode.toLowerCase()))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Auth mode '" + selectedMode + "' unsupported"));
}
// print address to file
if (null != addrfile) {
saveFile(addr, addrfile);
}
// print PID to file
if (null != pidfile) {
saveFile(System.getProperty("Pid"), pidfile);
}
// start the daemon
LOGGER.info("Binding to {}", addr);
try (EmbeddedDBusDaemon daemon = new EmbeddedDBusDaemon(address)) {
daemon.setSaslAuthMode(saslAuthMode);
daemon.startInForeground();
}
}
/**
* Create a 'NameAcquired' signal manually.
* This is required because the implementation in DBusNameAquired is for receiving of this signal only.
*
* @param _name name to announce
*
* @return signal
* @throws DBusException if signal creation fails
*/
private DBusSignal generateNameAcquiredSignal(String _name) throws DBusException {
return new DBusSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", "s", _name);
}
/**
* Create a 'NameOwnerChanged' signal manually.
* This is required because the implementation in DBusNameAquired is for receiving of this signal only.
*
* @param _name name to announce
* @param _oldOwner previous owner
* @param _newOwner new owner
*
* @return signal
* @throws DBusException if signal creation fails
*/
private DBusSignal generatedNameOwnerChangedSignal(String _name, String _oldOwner, String _newOwner) throws DBusException {
return new DBusSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", "sss", _name, _oldOwner, _newOwner);
}
public static class ConnectionStruct {
private final TransportConnection connection;
private String unique;
ConnectionStruct(TransportConnection _c) throws IOException {
connection = _c;
}
@Override
public String toString() {
return null == unique ? ":?-?" : unique;
}
}
public class DBusServer implements DBus, Introspectable, Peer {
private final String machineId;
private ConnectionStruct connStruct;
public DBusServer() {
String ascii;
try {
ascii = Hexdump.toAscii(MessageDigest.getInstance("MD5").digest(InetAddress.getLocalHost().getHostName().getBytes()));
} catch (NoSuchAlgorithmException | UnknownHostException _ex) {
ascii = this.hashCode() + "";
}
machineId = ascii;
}
@Override
public boolean isRemote() {
return false;
}
@Override
public String Hello() {
synchronized (connStruct) {
if (null != connStruct.unique) {
throw new AccessDenied("Connection has already sent a Hello message");
}
connStruct.unique = ":1." + nextUnique.incrementAndGet();
}
names.put(connStruct.unique, connStruct);
LOGGER.info("Client {} registered", connStruct.unique);
try {
send(connStruct, generateNameAcquiredSignal(connStruct.unique));
send(null, generatedNameOwnerChangedSignal(connStruct.unique, "", connStruct.unique));
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
}
return connStruct.unique;
}
@Override
public String[] ListNames() {
String[] ns;
Set<String> nss = names.keySet();
ns = nss.toArray(new String[0]);
return ns;
}
@Override
public boolean NameHasOwner(String _name) {
return names.containsKey(_name);
}
@Override
public String GetNameOwner(String _name) {
ConnectionStruct owner = names.get(_name);
String o;
if (null == owner) {
o = "";
} else {
o = owner.unique;
}
return o;
}
@Override
public UInt32 GetConnectionUnixUser(String _connectionName) {
return new UInt32(0);
}
@Override
public UInt32 StartServiceByName(String _name, UInt32 _flags) {
return new UInt32(0);
}
@Override
@SuppressWarnings("checkstyle:innerassignment")
public UInt32 RequestName(String _name, UInt32 _flags) {
boolean exists = false;
synchronized (names) {
if (!(exists = names.containsKey(_name))) {
names.put(_name, connStruct);
}
}
int rv;
if (exists) {
rv = DBus.DBUS_REQUEST_NAME_REPLY_EXISTS;
} else {
LOGGER.info("Client {} acquired name {}", connStruct.unique, _name);
rv = DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
try {
send(connStruct, generateNameAcquiredSignal(_name));
send(null, generatedNameOwnerChangedSignal(_name, "", connStruct.unique));
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
}
}
return new UInt32(rv);
}
@Override
public UInt32 ReleaseName(String _name) {
boolean exists = false;
synchronized (names) {
if (names.containsKey(_name) && names.get(_name).equals(connStruct)) {
exists = names.remove(_name) != null;
}
}
int rv;
if (!exists) {
rv = DBus.DBUS_RELEASE_NAME_REPLY_NON_EXISTANT;
} else {
LOGGER.info("Client {} acquired name {}", connStruct.unique, _name);
rv = DBus.DBUS_RELEASE_NAME_REPLY_RELEASED;
try {
send(connStruct, new NameLost("/org/freedesktop/DBus", _name));
send(null, new NameOwnerChanged("/org/freedesktop/DBus", _name, connStruct.unique, ""));
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
}
}
return new UInt32(rv);
}
@Override
public void AddMatch(String _matchrule) throws MatchRuleInvalid {
LOGGER.trace("Adding match rule: {}", _matchrule);
synchronized (sigrecips) {
if (!sigrecips.contains(connStruct)) {
sigrecips.add(connStruct);
}
}
}
@Override
public void RemoveMatch(String _matchrule) throws MatchRuleInvalid {
LOGGER.trace("Removing match rule: {}", _matchrule);
}
@Override
public String[] ListQueuedOwners(String _name) {
return new String[0];
}
@Override
public UInt32 GetConnectionUnixProcessID(String _connectionName) {
return new UInt32(0);
}
@Override
public Byte[] GetConnectionSELinuxSecurityContext(String _args) {
return new Byte[0];
}
@SuppressWarnings("unchecked")
private void handleMessage(ConnectionStruct _connStruct, Message _msg) throws DBusException {
LOGGER.trace("Handling message {} from {}", _msg, _connStruct.unique);
if (!(_msg instanceof MethodCall)) {
return;
}
Object[] args = _msg.getParameters();
Class<? extends Object>[] cs = new Class[args.length];
for (int i = 0; i < cs.length; i++) {
cs[i] = args[i].getClass();
}
java.lang.reflect.Method meth = null;
Object rv = null;
try {
meth = DBusServer.class.getMethod(_msg.getName(), cs);
try {
this.connStruct = _connStruct;
rv = meth.invoke(dbusServer, args);
if (null == rv) {
send(_connStruct, new MethodReturn("org.freedesktop.DBus", (MethodCall) _msg, null), true);
} else {
String sig = Marshalling.getDBusType(meth.getGenericReturnType())[0];
send(_connStruct, new MethodReturn("org.freedesktop.DBus", (MethodCall) _msg, sig, rv), true);
}
} catch (InvocationTargetException _exIte) {
LOGGER.debug("", _exIte);
send(_connStruct, new Error("org.freedesktop.DBus", _msg, _exIte.getCause()));
} catch (DBusExecutionException _exDnEe) {
LOGGER.debug("", _exDnEe);
send(_connStruct, new Error("org.freedesktop.DBus", _msg, _exDnEe));
} catch (Exception _ex) {
LOGGER.debug("", _ex);
send(_connStruct, new Error("org.freedesktop.DBus", _connStruct.unique,
"org.freedesktop.DBus.Error.GeneralError", _msg.getSerial(), "s", "An error occurred while calling " + _msg.getName()));
}
} catch (NoSuchMethodException _exNsm) {
send(_connStruct, new Error("org.freedesktop.DBus", _connStruct.unique,
"org.freedesktop.DBus.Error.UnknownMethod", _msg.getSerial(), "s", "This service does not support " + _msg.getName()));
}
}
@Override
public String getObjectPath() {
return null;
}
@Override
public String Introspect() {
return "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>\n"
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ " <interface name=\"org.freedesktop.DBus\">\n"
+ " <method name=\"RequestName\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"in\" type=\"u\"/>\n"
+ " <arg direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"ReleaseName\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"StartServiceByName\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"in\" type=\"u\"/>\n"
+ " <arg direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"Hello\">\n"
+ " <arg direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"NameHasOwner\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"ListNames\">\n"
+ " <arg direction=\"out\" type=\"as\"/>\n"
+ " </method>\n"
+ " <method name=\"ListActivatableNames\">\n"
+ " <arg direction=\"out\" type=\"as\"/>\n"
+ " </method>\n" + " <method name=\"AddMatch\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"RemoveMatch\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"GetNameOwner\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"ListQueuedOwners\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"as\"/>\n"
+ " </method>\n"
+ " <method name=\"GetConnectionUnixUser\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetConnectionUnixProcessID\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetConnectionSELinuxSecurityContext\">\n"
+ " <arg direction=\"in\" type=\"s\"/>\n"
+ " <arg direction=\"out\" type=\"ay\"/>\n"
+ " </method>\n"
+ " <method name=\"ReloadConfig\">\n"
+ " </method>\n"
+ " <signal name=\"NameOwnerChanged\">\n"
+ " <arg type=\"s\"/>\n"
+ " <arg type=\"s\"/>\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"NameLost\">\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"NameAcquired\">\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n"
+ " </interface>\n" + "</node>";
}
@Override
public void Ping() {
}
@Override
public String[] ListActivatableNames() {
return null;
}
@Override
public Map<String, Variant<?>> GetConnectionCredentials(String _busName) {
return null;
}
@Override
public Byte[] GetAdtAuditSessionData(String _busName) {
return null;
}
@Override
public void UpdateActivationEnvironment(Map<String, String>[] _environment) {
}
@Override
public String GetId() {
return null;
}
@Override
public String GetMachineId() {
return machineId;
}
}
public class DBusDaemonSenderThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final AtomicBoolean running = new AtomicBoolean(false); // switch running status when thread begins
public DBusDaemonSenderThread() {
setName(getClass().getSimpleName().replace('$', '-'));
}
@Override
public void run() {
logger.debug(">>>> Sender thread started <<<<");
running.set(true);
while (isRunning() && running.get()) {
logger.trace("Acquiring lock on outqueue and blocking for data");
// block on outqueue
try {
Pair<Message, WeakReference<ConnectionStruct>> pollFirst = outqueue.take();
if (pollFirst != null) {
ConnectionStruct connectionStruct = pollFirst.second.get();
if (connectionStruct != null) {
if (connectionStruct.connection.getChannel().isConnected()) {
logger.debug("<outqueue> Got message {} for {}", pollFirst.first, connectionStruct.unique);
try {
connectionStruct.connection.getWriter().writeMessage(pollFirst.first);
} catch (IOException _ex) {
logger.debug("Disconnecting client due to previous exception", _ex);
removeConnection(connectionStruct);
}
} else {
logger.warn("Connection to {} broken", pollFirst.first.getDestination());
removeConnection(connectionStruct);
}
} else {
logger.info("Discarding {} connection reaped", pollFirst.first);
}
}
} catch (InterruptedException _ex) {
logger.debug("Got interrupted", _ex);
}
}
logger.debug(">>>> Sender Thread terminated <<<<");
}
public synchronized void terminate() {
running.set(false);
interrupt();
}
}
public class DBusDaemonReaderThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private ConnectionStruct conn;
private final WeakReference<ConnectionStruct> weakconn;
private final AtomicBoolean running = new AtomicBoolean(false);
public DBusDaemonReaderThread(ConnectionStruct _conn) {
this.conn = _conn;
weakconn = new WeakReference<>(_conn);
setName(getClass().getSimpleName());
}
public void terminate() {
running.set(false);
}
@Override
public void run() {
logger.debug(">>>> Reader Thread started <<<<");
running.set(true);
while (isRunning() && running.get()) {
Message m = null;
try {
m = conn.connection.getReader().readMessage();
} catch (IOException _ex) {
LOGGER.debug("", _ex);
removeConnection(conn);
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
if (_ex instanceof FatalException) {
removeConnection(conn);
}
}
if (null != m) {
logMessage("Read {} from {}", m, conn.unique);
inqueue.add(new Pair<>(m, weakconn));
}
}
conn = null;
logger.debug(">>>> Reader Thread terminated <<<<");
}
}
static class Pair<A, B> {
private final A first;
private final B second;
Pair(A _first, B _second) {
first = _first;
second = _second;
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
@Override
public boolean equals(Object _obj) {
if (this == _obj) {
return true;
}
if (!(_obj instanceof Pair)) {
return false;
}
Pair<?, ?> other = (Pair<?, ?>) _obj;
return Objects.equals(first, other.first) && Objects.equals(second, other.second);
}
}
}

View file

@ -0,0 +1,224 @@
package org.freedesktop.dbus.bin;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode;
import org.freedesktop.dbus.connections.transports.TransportConnection;
import org.freedesktop.dbus.exceptions.AuthenticationException;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.SocketClosedException;
import org.freedesktop.dbus.utils.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Simple DBusDaemon implementation to use if no DBusDaemon is running on the OS level.
*/
public class EmbeddedDBusDaemon implements Closeable {
private static final Logger LOGGER = LoggerFactory.getLogger(EmbeddedDBusDaemon.class);
private final BusAddress address;
private DBusDaemon daemon;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final AtomicBoolean connectionReady = new AtomicBoolean(false);
private SaslAuthMode saslAuthMode;
private String unixSocketFileOwner;
private String unixSocketFileGroup;
private PosixFilePermission[] unixSocketFilePermissions;
public EmbeddedDBusDaemon(BusAddress _address) {
// create copy of address so manipulation happens later does not interfere with our instance
address = BusAddress.of(Objects.requireNonNull(_address, "Address required"));
}
public EmbeddedDBusDaemon(String _address) throws DBusException {
this(BusAddress.of(_address));
}
/**
* Shutdown the running DBusDaemon instance.
*/
@Override
public synchronized void close() throws IOException {
closed.set(true);
connectionReady.set(false);
if (daemon != null) {
daemon.close();
daemon = null;
}
}
/**
* Run the DBusDaemon in foreground.
* <p>
* This is a blocking operation.
*/
public void startInForeground() {
try {
startListening();
} catch (IOException | DBusException _ex) {
if (!closed.get()) {
throw new RuntimeException(_ex);
}
}
}
/**
* Start the DBusDaemon in background and returns immediately.
* <p>
* This method may return before the background thread is ready.
* To ensure the the background thread is running on return use {@link #startInBackgroundAndWait(long)}.
*/
public void startInBackground() {
Thread thread = new Thread(this::startInForeground);
String threadName = address.toString().replaceAll("^([^,]+),.+", "$1");
thread.setName("EmbeddedDBusDaemon-" + threadName);
thread.setDaemon(true);
thread.setUncaughtExceptionHandler((th, ex) -> LOGGER.error("Got uncaught exception", ex));
thread.start();
}
/**
* Starts the DBusDaemon in background.
* <p>
* Will wait up to the given period of milliseconds for the background thread to get ready.
* If given wait time exceeded, a {@link RuntimeException} is thrown.
*
* @param _maxWaitMillis maximum wait time in milliseconds
*/
public void startInBackgroundAndWait(long _maxWaitMillis) {
startInBackground();
Util.waitFor("EmbeddedDbusDaemon", this::isRunning, _maxWaitMillis, 100);
}
/**
* Whether the DBusDaemon is still running.
*
* @return true if running, false otherwise
*/
public synchronized boolean isRunning() {
return connectionReady.get() && daemon != null && daemon.isRunning();
}
/**
* The currently configured {@link SaslAuthMode}.
* When null is returned, the {@link SaslAuthMode} of the transport provider is used.
*
* @return {@link SaslAuthMode} or null
*/
public SaslAuthMode getSaslAuthMode() {
return saslAuthMode;
}
/**
* Use this to override the default authentication mode which would
* be used by the transport based on the {@link BusAddress}.
*
* @param _saslAuthMode auth mode, null to use default
*/
public void setSaslAuthMode(SaslAuthMode _saslAuthMode) {
saslAuthMode = _saslAuthMode;
}
/**
* The file owner for the created unix socket.<br>
* Ignored if TCP is used.<br>
* <br>
* Will only work if currently running JVM process user
* has suitable permissions to change the owner.
*
* @param _owner owner to set
*/
public void setUnixSocketOwner(String _owner) {
unixSocketFileOwner = _owner;
}
/**
* The file group for the created unix socket.<br>
* Ignored if TCP is used.<br>
* <br>
* Will only work if currently running JVM process user
* has suitable permissions to change the group.
*
* @param _group group to set
*/
public void setUnixSocketGroup(String _group) {
unixSocketFileGroup = _group;
}
/**
* The file permissions for the created unix socket.<br>
* Ignored if TCP is used or if the OS is Windows.<br>
* <br>
* Will only work if currently running JVM process user
* has suitable permissions to change the permissions.
*
* @param _permissions permissions to set
*/
public void setUnixSocketPermissions(PosixFilePermission... _permissions) {
unixSocketFilePermissions = _permissions;
}
private synchronized void setDaemonAndStart(AbstractTransport _transport) {
daemon = new DBusDaemon(_transport);
daemon.start();
}
/**
* Start listening for incoming connections.
* <p>
* Will throw {@link IllegalArgumentException} if a unsupported transport is used.
*
* @throws IOException when connection fails
* @throws DBusException when the provided bus address is wrong
*/
private void startListening() throws IOException, DBusException {
if (!TransportBuilder.getRegisteredBusTypes().contains(address.getBusType())) {
throw new IllegalArgumentException("Unknown or unsupported address type: " + address.getType());
}
LOGGER.debug("About to initialize transport on: {}", address);
try (AbstractTransport transport = TransportBuilder.create(address).configure()
.withUnixSocketFileOwner(unixSocketFileOwner)
.withUnixSocketFileGroup(unixSocketFileGroup)
.withUnixSocketFilePermissions(unixSocketFilePermissions)
.withAutoConnect(false)
.configureSasl().withAuthMode(getSaslAuthMode()).back()
.back()
.build()) {
setDaemonAndStart(transport);
// use tail-controlled loop so we at least try to get a client connection once
do {
try {
LOGGER.debug("Begin listening to: {}", transport);
connectionReady.set(true);
TransportConnection s = transport.listen();
daemon.addSock(s);
} catch (AuthenticationException _ex) {
LOGGER.error("Authentication failed", _ex);
} catch (SocketClosedException _ex) {
LOGGER.debug("Connection closed", _ex);
}
} while (daemon.isRunning());
}
}
}

View file

@ -0,0 +1,21 @@
package org.freedesktop.dbus.config;
/**
* Constant class containing all properties supported as system properties.
*
* @author hypfvieh
* @since v4.2.2 - 2023-01-20
*/
public final class DBusSysProps {
public static final String SYSPROP_DBUS_TEST_HOME_DIR = "DBUS_TEST_HOMEDIR";
public static final String DBUS_SYSTEM_BUS_ADDRESS = "DBUS_SYSTEM_BUS_ADDRESS";
public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket";
public static final String DBUS_SESSION_BUS_ADDRESS = "DBUS_SESSION_BUS_ADDRESS";
public static final String DBUS_MACHINE_ID_SYS_VAR = "DBUS_MACHINE_ID_LOCATION";
public static final String DBUS_SESSION_BUS_ADDRESS_MACOS = "DBUS_LAUNCHD_SESSION_BUS_SOCKET";
private DBusSysProps() {}
}

View file

@ -0,0 +1,284 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.InvalidBusAddressException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Defines an address to connect to DBus.
* The address will define which transport to use.
*/
public class BusAddress {
private static final Logger LOGGER = LoggerFactory.getLogger(BusAddress.class);
private String type;
private final Map<String, String> parameters = new LinkedHashMap<>();
/**
* Creates a new instance from String.
*
* @param _address address String
* @throws DBusException
*
* @deprecated Use BusAddress.of instead
*/
@Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18")
public BusAddress(String _address) throws DBusException {
if (_address == null || _address.isEmpty()) {
throw new DBusException("Bus address is blank");
}
String[] ss = _address.split(":", 2);
if (ss.length < 2) {
throw new DBusException("Bus address is invalid: " + _address);
}
type = ss[0] != null ? ss[0].toLowerCase(Locale.US) : null;
if (type == null) {
throw new DBusException("Unsupported transport type: " + ss[0]);
}
String[] ps = ss[1].split(",");
for (String p : ps) {
String[] kv = p.split("=", 2);
parameters.put(kv[0], kv[1]);
}
}
protected BusAddress(BusAddress _obj) {
if (_obj != null) {
parameters.putAll(_obj.parameters);
type = _obj.type;
}
}
/**
* Creates a copy of the given {@link BusAddress}.
* If given address is null, an empty {@link BusAddress} object is created.
*
* @param _address address to copy
* @return BusAddress
* @since 4.2.0 - 2022-07-18
*/
public static BusAddress of(BusAddress _address) {
return new BusAddress(_address);
}
/**
* Creates a new {@link BusAddress} from String.
*
* @param _address address String, never null or empty
*
* @return BusAddress
* @since 4.2.0 - 2022-07-18
*/
public static BusAddress of(String _address) {
if (_address == null || _address.isEmpty()) {
throw new InvalidBusAddressException("Bus address is blank");
}
BusAddress busAddress = new BusAddress((BusAddress) null);
LOGGER.trace("Parsing bus address: {}", _address);
String[] ss = _address.split(":", 2);
if (ss.length < 2) {
throw new InvalidBusAddressException("Bus address is invalid: " + _address);
}
busAddress.type = ss[0] != null ? ss[0].toLowerCase(Locale.US) : null;
if (busAddress.type == null) {
throw new InvalidBusAddressException("Unsupported transport type: " + ss[0]);
}
LOGGER.trace("Transport type: {}", busAddress.type);
String[] ps = ss[1].split(",");
for (String p : ps) {
String[] kv = p.split("=", 2);
busAddress.addParameter(kv[0], kv[1]);
}
LOGGER.trace("Transport options: {}", busAddress.parameters);
return busAddress;
}
/**
* Returns the transport type as found in the address.
*
* @return type
*/
public String getType() {
return type;
}
/**
* Returns the transport type in uppercase.
*
* @return type
*/
public String getBusType() {
return type == null ? null : type.toUpperCase(Locale.US);
}
/**
* Checks if this {@link BusAddress} is for the given bus type.<br>
* The given type will be compared case-insensitive.
* <br>
* e.g.
* <pre>
* isBusType("unix");
* </pre>
*
* @param _type to compare
*
* @return true if same type (case-insensitive), false if null or not same type
*
* @since 4.2.0 - 2022-07-20
*/
public boolean isBusType(String _type) {
return type != null && type.equalsIgnoreCase(_type);
}
/**
* True if this is a listening address.
* @return true if listening
*/
public boolean isListeningSocket() {
return parameters.containsKey("listen");
}
public String getGuid() {
return parameters.get("guid");
}
/**
* String version of the BusAddress.
* @return String
*
* @deprecated use {@link #toString()}
*/
@Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18")
public String getRawAddress() {
return toString();
}
@Override
public final String toString() {
return type + ":" + parameters.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(","));
}
/**
* True if this address represents a listening server address.
* @return true if server
*/
public boolean isServer() {
return isListeningSocket();
}
/**
* Add a parameter to the address.
* Adding multiple parameters with same name is not possible and will overwrite previous values.
*
* @param _parameter parameter name
* @param _value value
*
* @return this
* @since 4.2.0 - 2022-07-18
*/
public BusAddress addParameter(String _parameter, String _value) {
parameters.put(_parameter, _value);
return this;
}
/**
* Remove parameter with given name.
* If parameter does not exists, nothing will happen.
*
* @param _parameter parameter to remove
*
* @return this
* @since 4.2.0 - 2022-07-18
*/
public BusAddress removeParameter(String _parameter) {
parameters.remove(_parameter);
return this;
}
/**
* Checks if the given parameter is present.
*
* @param _parameter parameter to check
*
* @return true if parameter exists, false otherwise
* @since 4.2.2 - 2023-01-11
*/
public boolean hasParameter(String _parameter) {
return parameters.containsKey(_parameter);
}
/**
* Returns a read-only view of the parameters currently configured.
*
* @return Map, maybe empty
* @since 4.2.0 - 2022-07-18
*
* @deprecated will be removed in future, use {@link #getParameterValue(String)} or {@link #hasParameter(String)}
*/
@Deprecated(forRemoval = true, since = "4.2.2 - 2023-01-11")
public Map<String, String> getParameters() {
return Collections.unmodifiableMap(parameters);
}
/**
* Returns a the value of the given parameter.
* <p>
* When no value present, <code>null</code> is returned.
*
* @param _parameter parameter to get value for
*
* @return String or <code>null</code>
* @since 4.2.0 - 2022-07-19
*/
public String getParameterValue(String _parameter) {
return parameters.get(_parameter);
}
/**
* Returns a the value of the given parameter.
* <p>
* When no value present, the given default is returned.
*
* @param _parameter parameter to get value for
* @param _default default to return if parameter not set
*
* @return String or default
* @since 4.2.2 - 2023-01-11
*/
public String getParameterValue(String _parameter, String _default) {
return parameters.getOrDefault(_parameter, _default);
}
/**
* Creates a listening BusAddress if this instance is not already listening.
*
* @return new BusAddress or this
* @since 4.2.0 - 2022-07-18
*/
public BusAddress getListenerAddress() {
if (!isListeningSocket()) {
return new BusAddress(this).addParameter("listen", "true");
}
return this;
}
}

View file

@ -0,0 +1,58 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.messages.ExportedObject;
import org.freedesktop.dbus.utils.LoggingHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class FallbackContainer {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Map<String[], ExportedObject> fallbacks = new HashMap<>();
FallbackContainer() {
}
public synchronized void add(String _path, ExportedObject _eo) {
logger.debug("Adding fallback on {} of {}", _path, _eo);
fallbacks.put(_path.split("/"), _eo);
}
public synchronized void remove(String _path) {
logger.debug("Removing fallback on {}", _path);
fallbacks.remove(_path.split("/"));
}
public synchronized ExportedObject get(String _path) {
int best = 0;
ExportedObject bestobject = null;
String[] pathel = _path.split("/");
for (Map.Entry<String[], ExportedObject> entry : fallbacks.entrySet()) {
String[] fbpath = entry.getKey();
LoggingHelper.logIf(logger.isTraceEnabled(), () ->
logger.trace("Trying fallback path {} to match {}",
Arrays.deepToString(fbpath),
Arrays.deepToString(pathel))
);
int i;
for (i = 0; i < pathel.length && i < fbpath.length; i++) {
if (!pathel[i].equals(fbpath[i])) {
break;
}
}
if (i > 0 && i == fbpath.length && i > best) {
bestobject = entry.getValue();
}
logger.trace("Matches {} bestobject now {}", i, bestobject);
}
logger.debug("Found fallback for {} of {}", _path, bestobject);
return bestobject;
}
}

View file

@ -0,0 +1,59 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.errors.UnknownObject;
import org.freedesktop.dbus.messages.ExportedObject;
public class GlobalHandler implements org.freedesktop.dbus.interfaces.Peer, org.freedesktop.dbus.interfaces.Introspectable {
/**
*
*/
private final AbstractConnection connection;
private final String objectpath;
GlobalHandler(AbstractConnection _abstractConnection) {
connection = _abstractConnection;
this.objectpath = null;
}
GlobalHandler(AbstractConnection _abstractConnection, String _objectpath) {
connection = _abstractConnection;
this.objectpath = _objectpath;
}
@Override
public boolean isRemote() {
return false;
}
@Override
public void Ping() {
// nothing to do
}
@Override
public String Introspect() {
String intro = connection.getObjectTree().Introspect(objectpath);
if (null == intro) {
ExportedObject eo = connection.getFallbackContainer().get(objectpath);
if (null != eo) {
intro = eo.getIntrospectiondata();
}
}
if (null == intro) {
throw new UnknownObject("Introspecting on non-existant object");
} else {
return "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + intro;
}
}
@Override
public String getObjectPath() {
return objectpath;
}
@Override
public String GetMachineId() {
return connection.getMachineId();
}
}

View file

@ -0,0 +1,5 @@
package org.freedesktop.dbus.connections;
public interface IDisconnectAction {
void perform();
}

View file

@ -0,0 +1,39 @@
package org.freedesktop.dbus.connections;
import java.io.IOException;
/**
* Callback interface which can be used to get notified about connection losses.
*
* @author hypfvieh
* @version 4.1.0 - 2022-02-03
*/
public interface IDisconnectCallback {
/**
* Called when the connection is closed due to a connection error (e.g. stream closed).
*
* @param _ex exception which was thrown by transport
*/
default void disconnectOnError(IOException _ex) {}
/**
* Called when the disconnect was intended.
* @param _connectionId connection Id if this was a shared connection,
* null if last shared or non-shared connection
*/
default void requestedDisconnect(Integer _connectionId) {}
/**
* Called when a client disconnected (only if this is a server/listening connection).
*/
default void clientDisconnect() {}
/**
* Called when the transport throws an exception
* while connection was already terminating.
*
* @param _ex exception which was thrown by transport
*/
default void exceptionOnTerminate(IOException _ex) {}
}

View file

@ -0,0 +1,71 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.IllegalThreadPoolStateException;
import org.freedesktop.dbus.interfaces.FatalException;
import org.freedesktop.dbus.messages.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.RejectedExecutionException;
public class IncomingMessageThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private volatile boolean terminate;
private final AbstractConnection connection;
public IncomingMessageThread(AbstractConnection _connection, BusAddress _busAddress) {
Objects.requireNonNull(_connection);
connection = _connection;
setName("DBusConnection [listener=" + _busAddress.isListeningSocket() + "]");
setDaemon(true);
}
public void terminate() {
terminate = true;
interrupt();
}
@Override
public void run() {
Message msg;
while (!terminate) {
msg = null;
// read from the wire
try {
// this blocks on outgoing being non-empty or a message being available.
msg = connection.readIncoming();
if (msg != null) {
logger.trace("Got Incoming Message: {}", msg);
connection.handleMessage(msg);
}
} catch (DBusException | RejectedExecutionException | IllegalThreadPoolStateException _ex) {
if (_ex instanceof FatalException) {
if (terminate) { // requested termination, ignore failures
return;
}
logger.error("FatalException in connection thread", _ex);
if (connection.isConnected()) {
terminate = true;
if (_ex.getCause() instanceof IOException) {
connection.internalDisconnect((IOException) _ex.getCause());
} else {
connection.internalDisconnect(null);
}
}
return;
}
if (!terminate) { // only log exceptions if the connection was not intended to be closed
logger.error("Exception in connection thread", _ex);
}
}
}
}
}

View file

@ -0,0 +1,137 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.interfaces.DBus;
import org.freedesktop.dbus.interfaces.DBusSigHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* Add addresses of peers to a set which will watch for them to
* disappear and automatically remove them from the set.
*/
public class PeerSet implements Set<String>, DBusSigHandler<DBus.NameOwnerChanged> {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Set<String> addresses;
public PeerSet(DBusConnection _connection) {
addresses = new TreeSet<>();
try {
_connection.addSigHandler(new DBusMatchRule(DBus.NameOwnerChanged.class, null, null), this);
} catch (DBusException _ex) {
logger.debug("", _ex);
}
}
@Override
public void handle(DBus.NameOwnerChanged _noc) {
logger.debug("Received NameOwnerChanged({}, {}, {})", _noc.name, _noc.oldOwner, _noc.newOwner);
if ("".equals(_noc.newOwner) && addresses.contains(_noc.name)) {
remove(_noc.name);
}
}
@Override
public boolean add(String _address) {
logger.debug("Adding {}", _address);
synchronized (addresses) {
return addresses.add(_address);
}
}
@Override
public boolean addAll(Collection<? extends String> _addresses) {
synchronized (this.addresses) {
return this.addresses.addAll(_addresses);
}
}
@Override
public void clear() {
synchronized (addresses) {
addresses.clear();
}
}
@Override
public boolean contains(Object _o) {
return addresses.contains(_o);
}
@Override
public boolean containsAll(Collection<?> _os) {
return addresses.containsAll(_os);
}
@Override
public boolean equals(Object _o) {
if (_o instanceof PeerSet) {
return ((PeerSet) _o).addresses.equals(addresses);
} else {
return false;
}
}
@Override
public int hashCode() {
return addresses.hashCode();
}
@Override
public boolean isEmpty() {
return addresses.isEmpty();
}
@Override
public Iterator<String> iterator() {
return addresses.iterator();
}
@Override
public boolean remove(Object _o) {
logger.debug("Removing {}", _o);
synchronized (addresses) {
return addresses.remove(_o);
}
}
@Override
public boolean removeAll(Collection<?> _os) {
synchronized (addresses) {
return addresses.removeAll(_os);
}
}
@Override
public boolean retainAll(Collection<?> _os) {
synchronized (addresses) {
return addresses.retainAll(_os);
}
}
@Override
public int size() {
return addresses.size();
}
@Override
public Object[] toArray() {
synchronized (addresses) {
return addresses.toArray();
}
}
@Override
public <T> T[] toArray(T[] _a) {
synchronized (addresses) {
return addresses.toArray(_a);
}
}
}

View file

@ -0,0 +1,39 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.DBusAsyncReply;
import org.freedesktop.dbus.interfaces.CallbackHandler;
import org.freedesktop.dbus.messages.MethodCall;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PendingCallbackManager {
private final Map<MethodCall, CallbackHandler<? extends Object>> pendingCallbacks;
private final Map<MethodCall, DBusAsyncReply<?>> pendingCallbackReplys;
PendingCallbackManager() {
pendingCallbacks = new ConcurrentHashMap<>();
pendingCallbackReplys = new ConcurrentHashMap<>();
}
public synchronized void queueCallback(MethodCall _call, Method _method, CallbackHandler<?> _callback, AbstractConnection _connection) {
pendingCallbacks.put(_call, _callback);
pendingCallbackReplys.put(_call, new DBusAsyncReply<>(_call, _method, _connection));
}
public synchronized CallbackHandler<? extends Object> removeCallback(MethodCall _methodCall) {
pendingCallbackReplys.remove(_methodCall);
return pendingCallbacks.remove(_methodCall);
}
public synchronized CallbackHandler<? extends Object> getCallback(MethodCall _methodCall) {
return pendingCallbacks.get(_methodCall);
}
public synchronized DBusAsyncReply<?> getCallbackReply(MethodCall _methodCall) {
return pendingCallbackReplys.get(_methodCall);
}
}

View file

@ -0,0 +1,249 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfigBuilder;
import org.freedesktop.dbus.exceptions.IllegalThreadPoolStateException;
import org.freedesktop.dbus.utils.NameableThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Service providing threads for every type of message expected to be received by DBus.
*
* @author hypfvieh
* @version 4.1.0 - 2022-02-02
*/
public class ReceivingService {
static final int MAX_RETRIES = 50;
private final Logger logger = LoggerFactory.getLogger(getClass());
private boolean closed = false;
private final Map<ExecutorNames, ExecutorService> executors = new ConcurrentHashMap<>();
private final IThreadPoolRetryHandler retryHandler;
/**
* Creates a new instance.
*
* @param _rsCfg configuration
*/
ReceivingService(ReceivingServiceConfig _rsCfg) {
ReceivingServiceConfig rsCfg = Optional.ofNullable(_rsCfg).orElse(ReceivingServiceConfigBuilder.getDefaultConfig());
executors.put(ExecutorNames.SIGNAL,
Executors.newFixedThreadPool(rsCfg.getSignalThreadPoolSize(), new NameableThreadFactory("DBus-Signal-Receiver-", true, rsCfg.getSignalThreadPriority())));
executors.put(ExecutorNames.ERROR,
Executors.newFixedThreadPool(rsCfg.getErrorThreadPoolSize(), new NameableThreadFactory("DBus-Error-Receiver-", true, rsCfg.getErrorThreadPriority())));
// we need multiple threads here so recursive method calls are possible
executors.put(ExecutorNames.METHODCALL,
Executors.newFixedThreadPool(rsCfg.getMethodCallThreadPoolSize(), new NameableThreadFactory("DBus-MethodCall-Receiver-", true, rsCfg.getMethodCallThreadPriority())));
executors.put(ExecutorNames.METHODRETURN,
Executors.newFixedThreadPool(rsCfg.getMethodReturnThreadPoolSize(), new NameableThreadFactory("DBus-MethodReturn-Receiver-", true, rsCfg.getMethodReturnThreadPriority())));
retryHandler = rsCfg.getRetryHandler();
}
/**
* Execute a runnable which handles a signal.
*
* @param _r runnable
*
* @return retries, if any input was null -1 is returned
*/
int execSignalHandler(Runnable _r) {
return execOrFail(ExecutorNames.SIGNAL, _r);
}
/**
* Execute a runnable which handles an error.
*
* @param _r runnable
*
* @return retries, if any input was null -1 is returned
*/
int execErrorHandler(Runnable _r) {
return execOrFail(ExecutorNames.ERROR, _r);
}
/**
* Execute a runnable which handles a method call.
*
* @param _r runnable
*
* @return retries, if any input was null -1 is returned
*/
int execMethodCallHandler(Runnable _r) {
return execOrFail(ExecutorNames.METHODCALL, _r);
}
/**
* Execute a runnable which handles the return of a method.
*
* @param _r runnable
*
* @return retries, if any input was null -1 is returned
*/
int execMethodReturnHandler(Runnable _r) {
return execOrFail(ExecutorNames.METHODRETURN, _r);
}
/**
* Executes a runnable in a given executor.
* May retry execution if {@link ExecutorService} has thrown an exception and retry handler
* allows re-processing.
* When re-processing fails for {@value #MAX_RETRIES} or more retries, an error will be logged
* and the runnable will not be executed.
*
* @param _executor executor to use
* @param _r runnable
*
* @return retries, if any input was null -1 is returned
*/
int execOrFail(ExecutorNames _executor, Runnable _r) {
if (_r == null || _executor == null) { // ignore invalid runnables or executors
return -1;
}
int failCount = 0;
while (failCount < MAX_RETRIES) {
try {
ExecutorService exec = getExecutor(_executor);
if (exec == null) { // this should never happen, map is initialized in constructor
throw new IllegalThreadPoolStateException("No executor found for " + _executor);
} else if (closed || exec.isShutdown() || exec.isTerminated()) {
throw new IllegalThreadPoolStateException("Receiving service already closed");
}
exec.execute(_r);
break; // execution done, no retry needed
} catch (IllegalThreadPoolStateException _ex) { // just throw our exception
throw _ex;
} catch (Exception _ex) {
if (retryHandler == null) {
logger.error("Could not handle runnable for executor {}, runnable will be dropped", _executor, _ex);
break; // no handler, assume ignoring runnable is ok
}
failCount++;
if (!retryHandler.handle(_executor, _ex)) {
logger.trace("Ignoring unhandled runnable for executor {} due to {}, dropped by retry handler after {} retries", _executor, _ex.getClass().getName(), failCount);
break;
}
}
}
if (failCount >= MAX_RETRIES) {
logger.error("Could not handle runnable for executor {} after {} retries, runnable will be dropped", _executor, failCount);
}
return failCount;
}
/**
* Returns the executor or null.
*
* @param _executor executor to use
* @return executor or null
*/
ExecutorService getExecutor(ExecutorNames _executor) {
return executors.get(_executor);
}
/**
* Shutdown all executor services waiting up to the given timeout/unit.
*
* @param _timeout timeout
* @param _unit time unit
*/
public synchronized void shutdown(int _timeout, TimeUnit _unit) {
for (Entry<ExecutorNames, ExecutorService> es : executors.entrySet()) {
logger.debug("Shutting down executor: {}", es.getKey());
es.getValue().shutdown();
}
for (Entry<ExecutorNames, ExecutorService> es : executors.entrySet()) {
try {
es.getValue().awaitTermination(_timeout, _unit);
} catch (InterruptedException _ex) {
logger.debug("Interrupted while waiting for termination of executor");
}
}
closed = true;
}
/**
* Forcefully stop the executors.
*/
public synchronized void shutdownNow() {
for (Entry<ExecutorNames, ExecutorService> es : executors.entrySet()) {
if (!es.getValue().isTerminated()) {
logger.debug("Forcefully stopping {}", es.getKey());
es.getValue().shutdownNow();
}
}
closed = true;
}
/**
* Enum representing different executor services.
*
* @author hypfvieh
* @version 4.0.1 - 2022-02-02
*/
public enum ExecutorNames {
SIGNAL("SignalExecutor"),
ERROR("ErrorExecutor"),
METHODCALL("MethodCallExecutor"),
METHODRETURN("MethodReturnExecutor");
private final String description;
ExecutorNames(String _name) {
description = _name;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return description;
}
}
/**
* Interface which specifies a handler which will be called when the thread pool throws any exception.
*
* @author hypfvieh
* @since 4.2.0 - 2022-07-14
*/
@FunctionalInterface
public interface IThreadPoolRetryHandler {
/**
* Called to handle an exception.
* <p>
* This method should return true to retry execution or false to
* just ignore the error and drop the unhandled message.
* </p>
*
* @param _executor the executor which has thrown the exception
* @param _ex the exception which was thrown
*
* @return true to retry execution of the failed runnable, false to ignore runnable
*/
boolean handle(ExecutorNames _executor, Exception _ex);
}
}

View file

@ -0,0 +1,923 @@
package org.freedesktop.dbus.connections;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.AGREE_UNIX_FD;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.AUTH;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.BEGIN;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.CANCEL;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.DATA;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.ERROR;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.NEGOTIATE_UNIX_FD;
import static org.freedesktop.dbus.connections.SASL.SaslCommand.REJECTED;
import com.sun.security.auth.module.UnixSystem;
import org.freedesktop.dbus.config.DBusSysProps;
import org.freedesktop.dbus.connections.config.SaslConfig;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.AbstractUnixTransport;
import org.freedesktop.dbus.exceptions.AuthenticationException;
import org.freedesktop.dbus.exceptions.SocketClosedException;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.utils.Hexdump;
import org.freedesktop.dbus.utils.LoggingHelper;
import org.freedesktop.dbus.utils.TimeMeasure;
import org.freedesktop.dbus.utils.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.NetworkChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
public class SASL {
public static final int AUTH_NONE = 0;
public static final int AUTH_EXTERNAL = 1;
public static final int AUTH_SHA = 2;
public static final int AUTH_ANON = 4;
public static final int LOCK_TIMEOUT = 1000;
public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5;
public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2);
public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5;
public static final int COOKIE_TIMEOUT = 240;
public static final String COOKIE_CONTEXT = "org_freedesktop_java";
private static final int MAX_READ_BYTES = 64;
private static final Collator COL = Collator.getInstance();
static {
COL.setDecomposition(Collator.FULL_DECOMPOSITION);
COL.setStrength(Collator.PRIMARY);
}
private static final String SYSPROP_USER_HOME = System.getProperty("user.home");
private static final String DBUS_TEST_HOME_DIR = System.getProperty(DBusSysProps.SYSPROP_DBUS_TEST_HOME_DIR);
private static final File DBUS_KEYRINGS_DIR = new File(SYSPROP_USER_HOME, ".dbus-keyrings");
private static final Set<PosixFilePermission> BAD_FILE_PERMISSIONS =
Set.of(PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE,
PosixFilePermission.OTHERS_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE);
private String challenge = "";
private String cookie = "";
private final Logger logger = LoggerFactory.getLogger(getClass());
/** whether file descriptor passing is supported on the current connection. */
private boolean fileDescriptorSupported;
private final SaslConfig saslConfig;
/**
* Create a new SASL auth handler.
* Defaults to disable file descriptor passing.
*
* @deprecated should not be used as SASL configuration is not provided
*/
@Deprecated(since = "4.2.2 - 2023-02-03", forRemoval = true)
public SASL() {
this(SaslConfig.create());
}
/**
* Create a new SASL auth handler.
*
* @param _hasFileDescriptorSupport true to support file descriptor passing (usually only works with UNIX_SOCKET).
* @param _saslConfig SASL configuration
*/
public SASL(SaslConfig _saslConfig) {
saslConfig = Objects.requireNonNull(_saslConfig, "Sasl Configuration required");
}
private String findCookie(String _context, String _id) throws IOException {
File keyringDir = DBUS_KEYRINGS_DIR;
if (!Util.isBlank(DBUS_TEST_HOME_DIR)) {
keyringDir = new File(DBUS_TEST_HOME_DIR);
}
File f = new File(keyringDir, _context);
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f)))) {
String s = null;
String lCookie = null;
TimeMeasure tm = new TimeMeasure();
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long timestamp = Long.parseLong(line[1]);
if (line[0].equals(_id) && !(timestamp < 0 || (tm.getElapsedSeconds() + MAX_TIME_TRAVEL_SECONDS) < timestamp || tm.getElapsedSeconds() - EXPIRE_KEYS_TIMEOUT_SECONDS > timestamp)) {
lCookie = line[2];
break;
}
}
return lCookie;
}
}
@SuppressWarnings("checkstyle:emptyblock")
private void addCookie(String _context, String _id, long _timestamp, String _cookie) throws IOException {
File keyringDir = DBUS_KEYRINGS_DIR;
if (!Util.isBlank(DBUS_TEST_HOME_DIR)) {
keyringDir = new File(DBUS_TEST_HOME_DIR);
}
File cookiefile = new File(keyringDir, _context);
File lock = new File(keyringDir, _context + ".lock");
File temp = new File(keyringDir, _context + ".temp");
// ensure directory exists
if (!keyringDir.exists()) {
// directory did not exist, if we can create it, set proper permissions
if (keyringDir.mkdirs()) {
if (!Util.isWindows()) {
Util.setFilePermissions(keyringDir.toPath(), null, null, Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE));
}
} else {
throw new AuthenticationException("Unable to create keyring directory " + keyringDir);
}
} else { // directory already exists
if (!Util.isWindows()) { // verify permissions
Set<PosixFilePermission> currentPermissions = Files.getPosixFilePermissions(keyringDir.toPath(), LinkOption.NOFOLLOW_LINKS);
if (Util.collectionContainsAny(currentPermissions, BAD_FILE_PERMISSIONS)) {
if (saslConfig.isStrictCookiePermissions()) {
throw new AuthenticationException("Cannot authenticate using cookies: Permissions of directory " + lock + " should be 0700");
} else {
logger.warn("DBus keyring directory {} should have permissions 0700", lock);
}
}
}
}
// acquire lock
Util.waitFor("Lock file " + lock, () -> lock.createNewFile(), LOCK_TIMEOUT, 50);
// read old file
List<String> lines = new ArrayList<>();
if (cookiefile.exists()) {
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile)))) {
String s = null;
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long time = Long.parseLong(line[1]);
// expire stale cookies
if ((_timestamp - time) < COOKIE_TIMEOUT) {
lines.add(s);
}
}
}
}
// add cookie
lines.add(_id + " " + _timestamp + " " + _cookie);
// write temp file
Files.writeString(temp.toPath(), String.join(System.lineSeparator(), lines), Charset.defaultCharset(),
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
// atomically move to old file
if (!temp.renameTo(cookiefile)) {
cookiefile.delete();
temp.renameTo(cookiefile);
}
// remove lock
lock.delete();
}
/**
* Takes the string, encodes it as hex and then turns it into a string again.
* No, I don't know why either.
*/
private String stupidlyEncode(String _data) {
return Hexdump.toHex(_data.getBytes(), false);
}
private String stupidlyEncode(byte[] _data) {
return Hexdump.toHex(_data, false);
}
private byte getNibble(char _c) {
switch (_c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (byte) (_c - '0');
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return (byte) (_c - 'A' + 10);
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return (byte) (_c - 'a' + 10);
default:
return 0;
}
}
private String stupidlyDecode(String _data) {
char[] cs = new char[_data.length()];
char[] res = new char[cs.length / 2];
_data.getChars(0, _data.length(), cs, 0);
for (int i = 0, j = 0; j < res.length; i += 2, j++) {
int b = 0;
b |= getNibble(cs[i]) << 4;
b |= getNibble(cs[i + 1]);
res[j] = (char) b;
}
return new String(res);
}
public SASL.Command receive(SocketChannel _sock) throws IOException {
StringBuffer sb = new StringBuffer();
ByteBuffer buf = ByteBuffer.allocate(1); // only read one byte at a time to avoid reading to much (which would break the next message)
boolean runLoop = true;
int bytesRead = 0;
while (runLoop) {
int read = _sock.read(buf);
bytesRead += read;
buf.position(0);
if (read == -1) {
throw new SocketClosedException("Stream unexpectedly short (broken pipe)");
}
for (int i = buf.position(); i < read; i++) {
byte c = buf.get();
if (c == 0 || c == '\r') {
continue;
} else if (c == '\n') {
runLoop = false;
break;
} else {
sb.append((char) c);
}
}
buf.clear();
if (bytesRead > MAX_READ_BYTES) { // safe-guard to stop reading if no \n found
break;
}
}
logger.trace("received: {}", sb);
try {
return new Command(sb.toString());
} catch (Exception _ex) {
logger.error("Cannot create command.", _ex);
throw new AuthenticationException("Failed to authenticate.", _ex);
}
}
public void send(SocketChannel _sock, SaslCommand _command, String... _data) throws IOException {
StringBuffer sb = new StringBuffer();
sb.append(_command.name());
for (String s : _data) {
sb.append(' ');
sb.append(s);
}
sb.append('\r');
sb.append('\n');
logger.trace("sending: {}", sb);
_sock.write(ByteBuffer.wrap(sb.toString().getBytes()));
}
SaslResult doChallenge(int _auth, SASL.Command _c) throws IOException {
switch (_auth) {
case AUTH_SHA:
String[] reply = stupidlyDecode(_c.getData()).split(" ");
logger.trace(Arrays.toString(reply));
if (3 != reply.length) {
logger.debug("Reply is not length 3");
return SaslResult.ERROR;
}
String context = reply[0];
String id = reply[1];
final String serverchallenge = reply[2];
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException _ex) {
logger.debug("", _ex);
return SaslResult.ERROR;
}
byte[] buf = new byte[8];
// ensure we get a (more or less unique) positive long
long seed = Optional.of(System.nanoTime()).map(t -> t < 0 ? t * -1 : t).get();
Message.marshallintBig(seed, buf, 0, 8);
String clientchallenge = stupidlyEncode(md.digest(buf));
md.reset();
TimeMeasure tm = new TimeMeasure();
String lCookie = null;
while (lCookie == null && tm.getElapsed() < LOCK_TIMEOUT) {
lCookie = findCookie(context, id);
}
if (lCookie == null) {
logger.debug("Did not find a cookie in context {} with ID {}", context, id);
return SaslResult.ERROR;
}
String response = serverchallenge + ":" + clientchallenge + ":" + lCookie;
buf = md.digest(response.getBytes());
logger.trace("Response: {} hash: {}", response, Hexdump.format(buf));
response = stupidlyEncode(buf);
_c.setResponse(stupidlyEncode(clientchallenge + " " + response));
return SaslResult.OK;
default:
logger.debug("Not DBUS_COOKIE_SHA1 authtype.");
return SaslResult.ERROR;
}
}
SaslResult doResponse(int _auth, String _uid, String _kernelUid, SASL.Command _c) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException _ex) {
logger.error("", _ex);
return SaslResult.ERROR;
}
switch (_auth) {
case AUTH_NONE:
switch (_c.getMechs()) {
case AUTH_ANON:
return SaslResult.OK;
case AUTH_EXTERNAL:
if (0 == COL.compare(_uid, _c.getData()) && (null == _kernelUid || 0 == COL.compare(_uid, _kernelUid))) {
return SaslResult.OK;
} else {
return SaslResult.REJECT;
}
case AUTH_SHA:
String context = COOKIE_CONTEXT;
long id = System.currentTimeMillis();
byte[] buf = new byte[8];
Message.marshallintBig(id, buf, 0, 8);
challenge = stupidlyEncode(md.digest(buf));
Random r = new Random();
r.nextBytes(buf);
cookie = stupidlyEncode(md.digest(buf));
try {
addCookie(context, "" + id, id / 1000, cookie);
} catch (IOException _ex) {
logger.error("Error authenticating using cookie", _ex);
return SaslResult.ERROR;
}
logger.debug("Sending challenge: {} {} {}", context, id, challenge);
_c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge));
return SaslResult.OK;
default:
return SaslResult.ERROR;
}
case AUTH_SHA:
String[] response = stupidlyDecode(_c.getData()).split(" ");
if (response.length < 2) {
return SaslResult.ERROR;
}
String cchal = response[0];
String hash = response[1];
String prehash = challenge + ":" + cchal + ":" + cookie;
byte[] buf = md.digest(prehash.getBytes());
String posthash = stupidlyEncode(buf);
logger.debug("Authenticating Hash; data={} remote-hash={} local-hash={}", prehash, hash, posthash);
if (0 == COL.compare(posthash, hash)) {
return SaslResult.OK;
} else {
return SaslResult.ERROR;
}
default:
return SaslResult.ERROR;
}
}
public String[] convertAuthTypes(int _types) {
switch (_types) {
case AUTH_EXTERNAL:
return new String[] {
"EXTERNAL"
};
case AUTH_SHA:
return new String[] {
"DBUS_COOKIE_SHA1"
};
case AUTH_ANON:
return new String[] {
"ANONYMOUS"
};
case AUTH_SHA + AUTH_EXTERNAL:
return new String[] {
"EXTERNAL", "DBUS_COOKIE_SHA1"
};
case AUTH_SHA + AUTH_ANON:
return new String[] {
"ANONYMOUS", "DBUS_COOKIE_SHA1"
};
case AUTH_EXTERNAL + AUTH_ANON:
return new String[] {
"ANONYMOUS", "EXTERNAL"
};
case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
return new String[] {
"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"
};
default:
return new String[] {};
}
}
/**
* Performs SASL auth on the given socketchannel.
* Mode selects whether to run as a SASL server or client.
* Types is a bitmask of the available auth types.
*
* @param _config sasl configuration parameters
* @param _sock socket channel
* @param _transport transport
*
* @return true if the auth was successful and false if it failed.
* @throws IOException on failure
*/
public boolean auth(SocketChannel _sock, AbstractTransport _transport) throws IOException {
String luid = null;
String kernelUid = null;
long uid = saslConfig.getSaslUid().orElse(getUserId());
luid = stupidlyEncode("" + uid);
SASL.Command c;
int failed = 0;
int current = 0;
SaslAuthState state = SaslAuthState.INITIAL_STATE;
while (state != SaslAuthState.FINISHED && state != SaslAuthState.FAILED) {
logger.trace("Mode: {} AUTH state: {}", saslConfig.getMode(), state);
switch (saslConfig.getMode()) {
case CLIENT:
switch (state) {
case INITIAL_STATE:
_sock.write(ByteBuffer.wrap(new byte[] {0}));
send(_sock, AUTH);
state = SaslAuthState.WAIT_DATA;
break;
case WAIT_DATA:
c = receive(_sock);
switch (c.getCommand()) {
case DATA:
switch (doChallenge(current, c)) {
case CONTINUE:
send(_sock, DATA, c.getResponse());
break;
case OK:
send(_sock, DATA, c.getResponse());
state = SaslAuthState.WAIT_OK;
break;
case ERROR:
default:
send(_sock, ERROR, c.getResponse());
break;
}
break;
case REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
int retVal = handleReject(available, luid, _sock);
if (retVal == -1) {
state = SaslAuthState.FAILED;
} else {
current = retVal;
}
break;
case ERROR:
// when asking for file descriptor support, ERROR means FD support is not supported
if (state == SaslAuthState.NEGOTIATE_UNIX_FD) {
state = SaslAuthState.FINISHED;
logger.trace("File descriptors NOT supported by server");
fileDescriptorSupported = false;
send(_sock, BEGIN);
} else {
send(_sock, CANCEL);
state = SaslAuthState.WAIT_REJECT;
}
break;
case OK:
logger.trace("Authenticated");
state = SaslAuthState.AUTHENTICATED;
if (saslConfig.isFileDescriptorSupport()) {
state = SaslAuthState.WAIT_DATA;
logger.trace("Asking for file descriptor support");
// if authentication was successful, ask remote end for file descriptor support
send(_sock, NEGOTIATE_UNIX_FD);
} else {
state = SaslAuthState.FINISHED;
send(_sock, BEGIN);
}
break;
case AGREE_UNIX_FD:
if (saslConfig.isFileDescriptorSupport()) {
state = SaslAuthState.FINISHED;
logger.trace("File descriptors supported by server");
fileDescriptorSupported = true;
send(_sock, BEGIN);
}
break;
default:
send(_sock, ERROR, "Got invalid command");
break;
}
break;
case WAIT_OK:
c = receive(_sock);
switch (c.getCommand()) {
case OK:
send(_sock, BEGIN);
state = SaslAuthState.AUTHENTICATED;
break;
case ERROR:
case DATA:
send(_sock, CANCEL);
state = SaslAuthState.WAIT_REJECT;
break;
case REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
state = SaslAuthState.WAIT_DATA;
if (0 != (available & AUTH_EXTERNAL)) {
send(_sock, AUTH, "EXTERNAL", luid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(_sock, AUTH, "DBUS_COOKIE_SHA1", luid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(_sock, AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else {
state = SaslAuthState.FAILED;
}
break;
default:
send(_sock, ERROR, "Got invalid command");
break;
}
break;
case WAIT_REJECT:
c = receive(_sock);
switch (c.getCommand()) {
case REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
int retVal = handleReject(available, luid, _sock);
if (retVal == -1) {
state = SaslAuthState.FAILED;
} else {
current = retVal;
}
break;
default:
state = SaslAuthState.FAILED;
break;
}
break;
default:
state = SaslAuthState.FAILED;
}
break;
case SERVER:
switch (state) {
case INITIAL_STATE:
ByteBuffer buf = ByteBuffer.allocate(1);
if (_sock instanceof NetworkChannel) {
_sock.read(buf); // 0
state = SaslAuthState.WAIT_AUTH;
} else {
try {
int kuid = -1;
if (_transport instanceof AbstractUnixTransport) {
kuid = ((AbstractUnixTransport) _transport).getUid(_sock);
}
if (kuid >= 0) {
kernelUid = stupidlyEncode("" + kuid);
}
state = SaslAuthState.WAIT_AUTH;
} catch (SocketException _ex) {
state = SaslAuthState.FAILED;
}
}
break;
case WAIT_AUTH:
c = receive(_sock);
switch (c.getCommand()) {
case AUTH:
switch (doResponse(current, luid, kernelUid, c)) {
case CONTINUE:
send(_sock, DATA, c.getResponse());
current = c.getMechs();
state = SaslAuthState.WAIT_DATA;
break;
case OK:
send(_sock, SaslCommand.OK, saslConfig.getGuid());
state = SaslAuthState.WAIT_BEGIN;
current = 0;
break;
case REJECT:
default:
send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode()));
current = 0;
break;
}
break;
case ERROR:
send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode()));
break;
case BEGIN:
state = SaslAuthState.FAILED;
break;
default:
send(_sock, ERROR, "Got invalid command");
break;
}
break;
case WAIT_DATA:
c = receive(_sock);
switch (c.getCommand()) {
case DATA:
switch (doResponse(current, luid, kernelUid, c)) {
case CONTINUE:
send(_sock, DATA, c.getResponse());
state = SaslAuthState.WAIT_DATA;
break;
case OK:
send(_sock, SaslCommand.OK, saslConfig.getGuid());
state = SaslAuthState.WAIT_BEGIN;
current = 0;
break;
case REJECT:
default:
send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode()));
current = 0;
break;
}
break;
case ERROR:
case CANCEL:
send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode()));
state = SaslAuthState.WAIT_AUTH;
break;
case BEGIN:
state = SaslAuthState.FAILED;
break;
default:
send(_sock, ERROR, "Got invalid command");
break;
}
break;
case WAIT_BEGIN:
c = receive(_sock);
switch (c.getCommand()) {
case ERROR:
case CANCEL:
send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode()));
state = SaslAuthState.WAIT_AUTH;
break;
case BEGIN:
state = SaslAuthState.FINISHED;
break;
case NEGOTIATE_UNIX_FD:
logger.debug("File descriptor negotiation requested");
if (!saslConfig.isFileDescriptorSupport()) {
send(_sock, ERROR);
} else {
send(_sock, AGREE_UNIX_FD);
}
break;
default:
send(_sock, ERROR, "Got invalid command");
break;
}
break;
default:
state = SaslAuthState.FAILED;
}
break;
default:
return false;
}
}
return state == SaslAuthState.FINISHED;
}
public boolean isFileDescriptorSupported() {
return fileDescriptorSupported;
}
/**
* Handle reject of authentication.
*
* @param _available
* @param _luid
* @param _sock socketchannel
* @return current state or -1 if failed
* @throws IOException when sending fails
*/
private int handleReject(int _available, String _luid, SocketChannel _sock) throws IOException {
int current = -1;
if (0 != (_available & AUTH_EXTERNAL)) {
send(_sock, AUTH, "EXTERNAL", _luid);
current = AUTH_EXTERNAL;
} else if (0 != (_available & AUTH_SHA)) {
send(_sock, AUTH, "DBUS_COOKIE_SHA1", _luid);
current = AUTH_SHA;
} else if (0 != (_available & AUTH_ANON)) {
send(_sock, AUTH, "ANONYMOUS");
current = AUTH_ANON;
}
return current;
}
/**
* Tries to get the UID (user ID) of the current JVM process.
* Will always return 0 on windows.
* @return long
*/
private long getUserId() {
if (!Util.isWindows()) {
return new UnixSystem().getUid();
}
return 0;
}
public enum SaslMode {
SERVER, CLIENT;
}
public enum SaslCommand {
AUTH,
DATA,
REJECTED,
OK,
BEGIN,
CANCEL,
ERROR,
NEGOTIATE_UNIX_FD,
AGREE_UNIX_FD;
}
enum SaslAuthState {
INITIAL_STATE,
WAIT_DATA,
WAIT_OK,
WAIT_REJECT,
WAIT_AUTH,
WAIT_BEGIN,
AUTHENTICATED,
NEGOTIATE_UNIX_FD,
FINISHED,
FAILED;
}
public enum SaslResult {
OK,
CONTINUE,
ERROR,
REJECT;
}
public static class Command {
private final Logger logger = LoggerFactory.getLogger(getClass());
private SaslCommand command;
private int mechs;
private String data;
private String response;
public Command() {
}
public Command(String _s) throws IOException {
String[] ss = _s.split(" ");
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Creating command from: {}", Arrays.toString(ss)));
if (0 == COL.compare(ss[0], "OK")) {
command = SaslCommand.OK;
data = ss[1];
} else if (0 == COL.compare(ss[0], "AUTH")) {
command = AUTH;
if (ss.length > 1) {
if (0 == COL.compare(ss[1], "EXTERNAL")) {
mechs = AUTH_EXTERNAL;
} else if (0 == COL.compare(ss[1], "DBUS_COOKIE_SHA1")) {
mechs = AUTH_SHA;
} else if (0 == COL.compare(ss[1], "ANONYMOUS")) {
mechs = AUTH_ANON;
}
}
if (ss.length > 2) {
data = ss[2];
}
} else if (0 == COL.compare(ss[0], "DATA")) {
command = DATA;
data = ss[1];
} else if (0 == COL.compare(ss[0], "REJECTED")) {
command = REJECTED;
for (int i = 1; i < ss.length; i++) {
if (0 == COL.compare(ss[i], "EXTERNAL")) {
mechs |= AUTH_EXTERNAL;
} else if (0 == COL.compare(ss[i], "DBUS_COOKIE_SHA1")) {
mechs |= AUTH_SHA;
} else if (0 == COL.compare(ss[i], "ANONYMOUS")) {
mechs |= AUTH_ANON;
}
}
} else if (0 == COL.compare(ss[0], "BEGIN")) {
command = BEGIN;
} else if (0 == COL.compare(ss[0], "CANCEL")) {
command = CANCEL;
} else if (0 == COL.compare(ss[0], "ERROR")) {
command = ERROR;
data = ss[1];
} else if (0 == COL.compare(ss[0], "NEGOTIATE_UNIX_FD")) {
command = NEGOTIATE_UNIX_FD;
} else if (0 == COL.compare(ss[0], "AGREE_UNIX_FD")) {
command = AGREE_UNIX_FD;
} else {
throw new IOException("Invalid Command " + ss[0]);
}
logger.trace("Created command: {}", this);
}
public SaslCommand getCommand() {
return command;
}
public int getMechs() {
return mechs;
}
public String getData() {
return data;
}
public String getResponse() {
return response;
}
public void setResponse(String _s) {
response = _s;
}
@Override
public String toString() {
return "Command(" + command + ", " + mechs + ", " + data + ")";
}
}
}

View file

@ -0,0 +1,62 @@
package org.freedesktop.dbus.connections;
import org.freedesktop.dbus.messages.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class SenderThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(getClass());
private boolean terminate;
private final LinkedBlockingQueue<Message> outgoingQueue = new LinkedBlockingQueue<>();
private final AbstractConnection abstractConnection;
SenderThread(AbstractConnection _abstractConnection) {
abstractConnection = _abstractConnection;
setName("DBUS Sender Thread");
}
public void terminate() {
terminate = true;
interrupt();
}
public LinkedBlockingQueue<Message> getOutgoingQueue() {
return outgoingQueue;
}
@Override
public void run() {
Message m;
logger.trace("Monitoring outbound queue");
// block on the outbound queue and send from it
while (!terminate) {
try {
m = outgoingQueue.take();
if (m != null) {
abstractConnection.sendMessage(m);
}
} catch (InterruptedException _ex) {
if (!terminate) { // if terminate is true, shutdown was requested, do not log that
logger.warn("Interrupted while waiting for a message to send", _ex);
}
}
}
logger.debug("Flushing outbound queue and quitting");
// flush the outbound queue before disconnect.
while (!outgoingQueue.isEmpty()) {
Message poll = outgoingQueue.poll();
if (poll != null) {
abstractConnection.sendMessage(outgoingQueue.poll());
} else {
break;
}
}
}
}

View file

@ -0,0 +1,98 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.ReceivingService.IThreadPoolRetryHandler;
/**
* Bean which holds configuration for {@link org.freedesktop.dbus.connections.ReceivingService}.
*
* @author hypfvieh
* @since 4.2.0 - 2022-07-14
*/
public final class ReceivingServiceConfig {
private int signalThreadPoolSize = 1;
private int errorThreadPoolSize = 1;
private int methodCallThreadPoolSize = 4;
private int methodReturnThreadPoolSize = 1;
private int signalThreadPriority = Thread.NORM_PRIORITY;
private int methodCallThreadPriority = Thread.NORM_PRIORITY;
private int errorThreadPriority = Thread.NORM_PRIORITY;
private int methodReturnThreadPriority = Thread.NORM_PRIORITY;
private IThreadPoolRetryHandler retryHandler;
ReceivingServiceConfig() {
}
public int getSignalThreadPoolSize() {
return signalThreadPoolSize;
}
public int getErrorThreadPoolSize() {
return errorThreadPoolSize;
}
public int getMethodCallThreadPoolSize() {
return methodCallThreadPoolSize;
}
public int getMethodReturnThreadPoolSize() {
return methodReturnThreadPoolSize;
}
public int getSignalThreadPriority() {
return signalThreadPriority;
}
public int getMethodCallThreadPriority() {
return methodCallThreadPriority;
}
public int getErrorThreadPriority() {
return errorThreadPriority;
}
public int getMethodReturnThreadPriority() {
return methodReturnThreadPriority;
}
public IThreadPoolRetryHandler getRetryHandler() {
return retryHandler;
}
void setSignalThreadPoolSize(int _signalThreadPoolSize) {
signalThreadPoolSize = _signalThreadPoolSize;
}
void setErrorThreadPoolSize(int _errorThreadPoolSize) {
errorThreadPoolSize = _errorThreadPoolSize;
}
void setMethodCallThreadPoolSize(int _methodCallThreadPoolSize) {
methodCallThreadPoolSize = _methodCallThreadPoolSize;
}
void setMethodReturnThreadPoolSize(int _methodReturnThreadPoolSize) {
methodReturnThreadPoolSize = _methodReturnThreadPoolSize;
}
void setSignalThreadPriority(int _signalThreadPriority) {
signalThreadPriority = _signalThreadPriority;
}
void setMethodCallThreadPriority(int _methodCallThreadPriority) {
methodCallThreadPriority = _methodCallThreadPriority;
}
void setErrorThreadPriority(int _errorThreadPriority) {
errorThreadPriority = _errorThreadPriority;
}
void setMethodReturnThreadPriority(int _methodReturnThreadPriority) {
methodReturnThreadPriority = _methodReturnThreadPriority;
}
void setRetryHandler(IThreadPoolRetryHandler _retryHandler) {
retryHandler = _retryHandler;
}
}

View file

@ -0,0 +1,207 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.ReceivingService;
import org.freedesktop.dbus.connections.ReceivingService.ExecutorNames;
import org.freedesktop.dbus.connections.ReceivingService.IThreadPoolRetryHandler;
import org.freedesktop.dbus.connections.impl.BaseConnectionBuilder;
import org.freedesktop.dbus.utils.Util;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* Configuration builder to configure {@link ReceivingService}.
* Only intended to be used in combination with {@link BaseConnectionBuilder}
*
* @author hypfvieh
*
* @param <R> BaseConnectionBuilder type
* @since 4.2.0 - 2022-07-14
*/
public final class ReceivingServiceConfigBuilder<R extends BaseConnectionBuilder<?, ?>> {
public static final int DEFAULT_HANDLER_RETRIES = 10;
private static final ReceivingServiceConfig DEFAULT_CFG = new ReceivingServiceConfig();
private static final IThreadPoolRetryHandler DEFAULT_RETRYHANDLER = new IThreadPoolRetryHandler() {
private AtomicInteger retries = new AtomicInteger(0);
@Override
public boolean handle(ExecutorNames _executor, Exception _ex) {
if (retries.incrementAndGet() < DEFAULT_HANDLER_RETRIES) {
return true;
}
LoggerFactory.getLogger(ReceivingService.class).error("Dropping runnable for {}, retry failed for more than {} iterations, cause:", _executor, DEFAULT_HANDLER_RETRIES, _ex);
return false;
}
};
private final Supplier<R> connectionBuilder;
private final ReceivingServiceConfig config = new ReceivingServiceConfig();
public ReceivingServiceConfigBuilder(Supplier<R> _bldr) {
connectionBuilder = _bldr;
config.setRetryHandler(DEFAULT_RETRYHANDLER);
}
/**
* Set the size of the thread-pool used to handle signals from the bus.
* Caution: Using thread-pool size &gt; 1 may cause signals to be handled out-of-order
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
*/
public ReceivingServiceConfigBuilder<R> withSignalThreadCount(int _threads) {
config.setSignalThreadPoolSize(Math.max(1, _threads));
return this;
}
/**
* Set the size of the thread-pool used to handle error messages received on the bus.
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
*/
public ReceivingServiceConfigBuilder<R> withErrorHandlerThreadCount(int _threads) {
config.setErrorThreadPoolSize(Math.max(1, _threads));
return this;
}
/**
* Set the size of the thread-pool used to handle methods calls previously sent to the bus.
* The thread pool size has to be &gt; 1 to handle recursive calls.
* <p>
* Default: 4
*
* @param _threads int &gt;= 1
* @return this
*/
public ReceivingServiceConfigBuilder<R> withMethodCallThreadCount(int _threads) {
config.setMethodCallThreadPoolSize(Math.max(1, _threads));
return this;
}
/**
* Set the size of the thread-pool used to handle method return values received on the bus.
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
*/
public ReceivingServiceConfigBuilder<R> withMethodReturnThreadCount(int _threads) {
config.setMethodReturnThreadPoolSize(Math.max(1, _threads));
return this;
}
/**
* Sets the thread priority of the created signal thread(s).
* <p>
* Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY});
*
* @param _priority int &gt;={@value Thread#MIN_PRIORITY} and &lt;= {@value Thread#MAX_PRIORITY}
* @return this
*
* @throws IllegalArgumentException when value is out ouf range (value &lt; {@value Thread#MIN_PRIORITY} &amp;&amp; &gt; {@value Thread#MAX_PRIORITY})
*/
public ReceivingServiceConfigBuilder<R> withSignalThreadPriority(int _priority) {
config.setSignalThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY));
return this;
}
/**
* Sets the thread priority of the created signal thread(s).
* <p>
* Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY});
*
* @param _priority int &gt;={@value Thread#MIN_PRIORITY} and &lt;= {@value Thread#MAX_PRIORITY}
* @return this
*
* @throws IllegalArgumentException when value is out ouf range (value &lt; {@value Thread#MIN_PRIORITY} &amp;&amp; &gt; {@value Thread#MAX_PRIORITY})
*/
public ReceivingServiceConfigBuilder<R> withErrorThreadPriority(int _priority) {
config.setErrorThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY));
return this;
}
/**
* Sets the thread priority of the created signal thread(s).
* <p>
* Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY});
*
* @param _priority int &gt;={@value Thread#MIN_PRIORITY} and &lt;= {@value Thread#MAX_PRIORITY}
* @return this
*
* @throws IllegalArgumentException when value is out ouf range (value &lt; {@value Thread#MIN_PRIORITY} &amp;&amp; &gt; {@value Thread#MAX_PRIORITY})
*/
public ReceivingServiceConfigBuilder<R> withMethedCallThreadPriority(int _priority) {
config.setMethodCallThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY));
return this;
}
/**
* Sets the thread priority of the created signal thread(s).
* <p>
* Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY});
*
* @param _priority int &gt;={@value Thread#MIN_PRIORITY} and &lt;= {@value Thread#MAX_PRIORITY}
* @return this
*
* @throws IllegalArgumentException when value is out ouf range (value &lt; {@value Thread#MIN_PRIORITY} &amp;&amp; &gt; {@value Thread#MAX_PRIORITY})
*/
public ReceivingServiceConfigBuilder<R> withMethodReturnThreadPriority(int _priority) {
config.setMethodReturnThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY));
return this;
}
/**
* Sets the retry handler which should be called when executing a runnable in {@link ReceivingService} thread pools fail.
* <p>
* Defaults to an implementation retrying executing the runnable up to ten times.
* If <code>null</code> is given, retrying will be disabled (but error will be logged).
*
* @param _handler handler to use
* @return this
*/
public ReceivingServiceConfigBuilder<R> withRetryHandler(IThreadPoolRetryHandler _handler) {
config.setRetryHandler(_handler);
return this;
}
/**
* Returns the configured {@link ReceivingServiceConfig} instance.
* @return config never null
*/
public ReceivingServiceConfig build() {
return config;
}
/**
* Returns the used ConnectionBuilder for the connection for further configuration.
* @return connection builder
*/
public R connectionConfig() {
return connectionBuilder.get();
}
/**
* Returns the default configuration used for {@link ReceivingService}.
* @return default config
*/
public static ReceivingServiceConfig getDefaultConfig() {
return DEFAULT_CFG;
}
/**
* Returns the default retry handler used for {@link ReceivingService}.
* @return default handler
*/
public static IThreadPoolRetryHandler getDefaultRetryHandler() {
return DEFAULT_RETRYHANDLER;
}
}

View file

@ -0,0 +1,114 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.SASL;
import org.freedesktop.dbus.connections.SASL.SaslMode;
import java.util.OptionalLong;
/**
* Bean contains configuration for SASL authentication.
*
* @author hypfvieh
*
* @since 4.2.0 - 2022-07-22
*/
public class SaslConfig {
private SaslMode mode;
private int authMode;
private String guid;
private OptionalLong saslUid;
private boolean strictCookiePermissions;
private boolean fileDescriptorSupport;
SaslConfig() {
mode = SASL.SaslMode.CLIENT;
authMode = SASL.AUTH_NONE;
saslUid = OptionalLong.empty();
}
/**
* Creates a new empty SaslConfig object
* @return SaslConfig
* @deprecated only intended for internal backward compatibility, will be removed soon
*/
@Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03")
public static SaslConfig create() {
return new SaslConfig();
}
public SaslMode getMode() {
return mode;
}
public void setMode(SaslMode _mode) {
mode = _mode;
}
public int getAuthMode() {
return authMode;
}
public void setAuthMode(int _types) {
authMode = _types;
}
public String getGuid() {
return guid;
}
public void setGuid(String _guid) {
guid = _guid;
}
public OptionalLong getSaslUid() {
return saslUid;
}
public void setSaslUid(OptionalLong _saslUid) {
saslUid = _saslUid;
}
/**
* Whether the permissions of the cookie files (used for DBUS_COOKIE_SHA1) should be checked.<br>
* Cookie permission check will only be used on Linux/Unix like OSes.
*
* @return boolean
* @since v4.2.2 - 2023-02-03
*/
public boolean isStrictCookiePermissions() {
return strictCookiePermissions;
}
/**
* Enable/disable checking of file permissions of the cookie files (used for DBUS_COOKIE_SHA1).<br>
* Cookie permission check will only be used on Linux/Unix like OSes.
*
* @return boolean
* @since v4.2.2 - 2023-02-03
*/
public void setStrictCookiePermissions(boolean _strictCookiePermissions) {
strictCookiePermissions = _strictCookiePermissions;
}
/**
* Whether file descriptor passing is allowed.
*
* @return boolean
* @since v4.2.2 - 2023-02-03
*/
public boolean isFileDescriptorSupport() {
return fileDescriptorSupport;
}
/**
* Enable/disable support of file descriptor passing.
*
* @return boolean
* @since v4.2.2 - 2023-02-03
*/
public void setFileDescriptorSupport(boolean _fileDescriptorSupport) {
fileDescriptorSupport = _fileDescriptorSupport;
}
}

View file

@ -0,0 +1,84 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode;
import java.util.OptionalLong;
import java.util.function.Supplier;
/**
* Configuration used to setup a sasl authentication.
*
* @author hypfvieh
* @since v4.2.2 - 2023-02-03
*/
public final class SaslConfigBuilder<R> {
private final SaslConfig saslConfig;
private final Supplier<TransportConfigBuilder<?, R>> transportBuilder;
SaslConfigBuilder(SaslConfig _saslConfig, Supplier<TransportConfigBuilder<?, R>> _transportBuilder) {
saslConfig = _saslConfig;
transportBuilder = _transportBuilder;
}
/**
* Return to the previous builder.
* <p>
* This allows you to return from the this builder to the builder which
* started this builder so you can continue using the previous builder.
* </p>
*
* @return previous builder, maybe null
*/
public TransportConfigBuilder<?, R> back() {
return transportBuilder.get();
}
/**
* Setup the authentication mode to use. <br>
* <code>null</code> values will be ignored.
*
* @param _types auth mode to set
* @return this
*/
public SaslConfigBuilder<R> withAuthMode(SaslAuthMode _types) {
if (_types != null) {
saslConfig.setAuthMode(_types.getAuthMode());
}
return this;
}
/**
* Setup the user ID to use for authentication when using unix sockets.<br>
* Will default to the user ID of the user running the current process.
*
* @param _guid guid to use
* @return this
*/
public SaslConfigBuilder<R> withSaslUid(Long _saslUid) {
saslConfig.setSaslUid(OptionalLong.of(_saslUid));
return this;
}
/**
* Enable/disable checking of file permissions of the cookie files (used for DBUS_COOKIE_SHA1).<br>
* Cookie permission check will only be used on Linux/Unix like OSes.<br>
* Default is false (no strict checking).
*
* @param _strictCookiePermissions boolean
* @return this
*/
public SaslConfigBuilder<R> withStrictCookiePermissions(boolean _strictCookiePermissions) {
saslConfig.setStrictCookiePermissions(_strictCookiePermissions);
return this;
}
/**
* Returns the created configuration.
* @return SaslConfig
*/
public SaslConfig build() {
return saslConfig;
}
}

View file

@ -0,0 +1,157 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.utils.Util;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
/**
* Configuration used to setup a transport.
*
* @author hypfvieh
* @since v4.2.0 - 2022-07-21
*/
public final class TransportConfig {
private final SaslConfig saslConfig;
private BusAddress busAddress;
private Consumer<AbstractTransport> preConnectCallback;
private int timeout = 10000;
private boolean autoConnect = true;
/** user to set on socket file if this is a server transport (null to do nothing). */
private String fileOwner;
/** group to set on socket file if this is a server transport (null to do nothing). */
private String fileGroup;
/**
* Unix file permissions to set on socket file if this is a server transport (ignored on Windows, does nothing if
* null)
*/
private Set<PosixFilePermission> fileUnixPermissions;
/**
* Contains additional configuration where no direct getter/setter is available for.
*/
private Map<String, Object> additionalConfig = new LinkedHashMap<>();
public TransportConfig(BusAddress _address) {
busAddress = _address;
saslConfig = new SaslConfig();
}
public TransportConfig() {
this(null);
}
public BusAddress getBusAddress() {
return busAddress;
}
public void setBusAddress(BusAddress _busAddress) {
busAddress = Objects.requireNonNull(_busAddress, "BusAddress required");
}
public void setListening(boolean _listen) {
updateBusAddress(_listen);
}
public boolean isListening() {
return busAddress != null && busAddress.isListeningSocket();
}
public Consumer<AbstractTransport> getPreConnectCallback() {
return preConnectCallback;
}
public void setPreConnectCallback(Consumer<AbstractTransport> _preConnectCallback) {
preConnectCallback = _preConnectCallback;
}
public boolean isAutoConnect() {
return autoConnect;
}
public void setAutoConnect(boolean _autoConnect) {
autoConnect = _autoConnect;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int _timeout) {
timeout = _timeout;
}
public String getFileOwner() {
return fileOwner;
}
public void setFileOwner(String _fileOwner) {
fileOwner = _fileOwner;
}
public String getFileGroup() {
return fileGroup;
}
public void setFileGroup(String _fileGroup) {
fileGroup = _fileGroup;
}
public Set<PosixFilePermission> getFileUnixPermissions() {
return fileUnixPermissions;
}
public void setFileUnixPermissions(PosixFilePermission... _permissions) {
if (Util.isWindows()) {
return;
}
if (_permissions == null || _permissions.length < 1) {
return;
}
fileUnixPermissions = new LinkedHashSet<>(Arrays.asList(_permissions));
}
public Map<String, Object> getAdditionalConfig() {
return additionalConfig;
}
public void setAdditionalConfig(Map<String, Object> _additionalConfig) {
additionalConfig = _additionalConfig;
}
public SaslConfig getSaslConfig() {
return saslConfig;
}
/**
* Toggles the busaddress to be a listening (server) or non listening (client) connection.
* @param _listening true to be a server connection
*/
void updateBusAddress(boolean _listening) {
if (busAddress == null) {
return;
}
if (!busAddress.isListeningSocket() && _listening) { // not a listening address, but should be one
busAddress.addParameter("listen", "true");
} else if (busAddress.isListeningSocket() && !_listening) { // listening address, but should not be one
busAddress.removeParameter("listen");
}
}
}

View file

@ -0,0 +1,275 @@
package org.freedesktop.dbus.connections.config;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class TransportConfigBuilder<X extends TransportConfigBuilder<?, R>, R> {
private final Supplier<R> connectionBuilder;
private TransportConfig config = new TransportConfig();
private final SaslConfigBuilder<R> saslConfigBuilder;
public TransportConfigBuilder(Supplier<R> _sup) {
connectionBuilder = _sup;
saslConfigBuilder = new SaslConfigBuilder<>(config.getSaslConfig(), () -> this);
}
/**
* Return ourselves.
* @return concrete version of this
*/
@SuppressWarnings("unchecked")
X self() {
return (X) this;
}
/**
* Use the predefined TransportConfig.
* <p>
* Using this will override any previous configuration and replaces
* the internal configuration object with the given instance.
* </p>
* @param _config configuration, never null
*
* @return this
*/
public X withConfig(TransportConfig _config) {
Objects.requireNonNull(_config, "TransportConfig required");
config = _config;
return self();
}
/**
* Set the {@link BusAddress} which should be used for the connection.
*
* @param _address address to use
*
* @return this
*/
public X withBusAddress(BusAddress _address) {
config.setBusAddress(Objects.requireNonNull(_address, "BusAddress required"));
return self();
}
/**
* Returns the currently configured BusAddress.
*
* @return BusAddress, maybe null
*/
public BusAddress getBusAddress() {
return config.getBusAddress();
}
/**
* Set a callback which will be called right before the connection to the transport is established.
* <p>
* The given consumer will receive the created {@link AbstractTransport} object which is not yet
* connected. A callback should <b>NEVER</b> connect the transport, but is allowed to do further
* configuration if needed.
* </p>
*
* @param _callback consumer to call, null to remove any callback
*
* @return this
*/
public X withPreConnectCallback(Consumer<AbstractTransport> _callback) {
config.setPreConnectCallback(_callback);
return self();
}
/**
* Instantly connect to DBus when {@link #build()} is called.
* <p>
* default: true
*
* @param _connect boolean
*
* @return this
*/
public X withAutoConnect(boolean _connect) {
config.setAutoConnect(_connect);
return self();
}
/**
* Set a different SASL authentication mode.
* <p>
* Usually when a unixsocket based transport is used, {@link SaslAuthMode#AUTH_EXTERNAL} will be used.
* For TCP based transport {@link SaslAuthMode#AUTH_COOKIE} will be used.
* <p>
*
* @param _authMode authmode to use, if null is given, default mode will be used
*
* @return this
* @deprecated use {@link #configureSasl()} instead
*/
@Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03")
public X withSaslAuthMode(SaslAuthMode _authMode) {
configureSasl().withAuthMode(_authMode);
return self();
}
/**
* Switch to the {@link SaslConfigBuilder} to configure the SASL authentication mechanism.<br>
* Use {@link SaslConfigBuilder#back()} to return to this builder when finished.
*
* @return SaslConfigBuilder
*/
public SaslConfigBuilder<R> configureSasl() {
return saslConfigBuilder;
}
/**
* Use true to use the transport as listener (server).
*
* @param _listen true to be a listening connection
*
* @return this
*/
public X withListening(boolean _listen) {
config.setListening(_listen);
return self();
}
/**
* Setup a timeout for the transport.
* <p>
* This option might not be used by every transport
* (e.g. unix sockets do not support a timeout).
* Timeout cannot be less than zero.
* </p>
*
* @param _timeout true to be a listening connection
*
* @return this
*/
public X withTimeout(int _timeout) {
if (_timeout >= 0) {
config.setTimeout(_timeout);
}
return self();
}
/**
* Set to UID to present during SASL authentication.
* <p>
* Default is the user of the running JVM process on Unix-like operating systems. On Windows, the default is zero.<br><br>
*
* @param _saslUid UID to set, if a negative long is given the default is used
*
* @return this
* @deprecated use {@link #configureSasl()} instead
*/
@Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03")
public X withSaslUid(long _saslUid) {
configureSasl().withSaslUid(_saslUid);
return self();
}
/**
* The owner of the socket file if a unix socket is used and this is a server transport.
* <p>
* Default is the user of the running JVM process.<br><br>
* <b>Please note:</b><br>
* The running process user has to have suitable permissions to change the owner
* of the file. Otherwise the file owner will not be changed!
*
* @param _user user to set, if null is given JVM process user is used
*
* @return this
*/
public X withUnixSocketFileOwner(String _user) {
config.setFileOwner(_user);
return self();
}
/**
* The group of the socket file if a unix socket is used and this is a server transport.
* <p>
* Default is the group of the running JVM process.<br><br>
* <b>Please note:</b><br>
* The running process user has to have suitable permissions to change the group
* of the file. Otherwise the file group will not be changed!
*
* @param _group group to set, if null is given JVM process group is used
*
* @return this
*/
public X withUnixSocketFileGroup(String _group) {
config.setFileGroup(_group);
return self();
}
/**
* The permissions which will be set on socket file if a unix socket is used and this is a server transport.
* <p>
* This method does nothing when used on windows systems.
* <b>Please note:</b><br>
* The running process user has to have suitable permissions to change the permissions
* of the file. Otherwise the file permissions will not be changed!
*
* @param _permissions permissions to set, if null is given default permissions will be used
*
* @return this
*/
public X withUnixSocketFilePermissions(PosixFilePermission... _permissions) {
config.setFileUnixPermissions(_permissions);
return self();
}
/**
* Adds an additional config key to the transport config.<br>
* Will overwrite value if key exists.
*
* @param _key key to use
* @param _value value to use
*
* @return this
*/
public X withAdditionalConfig(String _key, Object _value) {
config.getAdditionalConfig().put(_key, _value);
return self();
}
/**
* Removes an additional config key to of transport config.<br>
* Will do nothing if key does not exist.
*
* @param _key key to remove
*
* @return this
*/
public X withRemoveAdditionalConfig(String _key) {
config.getAdditionalConfig().remove(_key);
return self();
}
/**
* Return to the previous builder.
* <p>
* This allows you to return from the this builder to the builder which
* started this builder so you can continue using the previous builder.
* </p>
*
* @return previous builder, maybe null
*/
public R back() {
return connectionBuilder != null ? connectionBuilder.get() : null;
}
/**
* Returns the transport config.
* @return config
*/
public TransportConfig build() {
return config;
}
}

View file

@ -0,0 +1,211 @@
package org.freedesktop.dbus.connections.impl;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.IDisconnectCallback;
import org.freedesktop.dbus.connections.ReceivingService;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfigBuilder;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.connections.config.TransportConfigBuilder;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.Message.Endian;
import java.nio.ByteOrder;
/**
* Base class for connection builders containing commonly used options.
*
* @author hypfvieh
* @since 4.2.0 - 2022-07-13
*
* @param <R> concrete type of connection builder
*/
public abstract class BaseConnectionBuilder<R extends BaseConnectionBuilder<R, C>, C extends AbstractConnection> {
private final Class<R> returnType;
private boolean weakReference = false;
private byte endianess = getSystemEndianness();
private IDisconnectCallback disconnectCallback;
private final ReceivingServiceConfigBuilder<R> rsConfigBuilder;
private final TransportConfigBuilder<?, R> transportConfigBuilder;
protected BaseConnectionBuilder(Class<R> _returnType, BusAddress _address) {
returnType = _returnType;
rsConfigBuilder = new ReceivingServiceConfigBuilder<>(() -> self());
transportConfigBuilder = new TransportConfigBuilder<>(() -> self());
transportConfigBuilder.withBusAddress(_address);
}
/**
* Return ourselves.
* @return concrete version of this
*/
R self() {
return returnType.cast(this);
}
/**
* Creates the configuration to use for {@link ReceivingService}.
*
* @return config
*/
protected ReceivingServiceConfig buildThreadConfig() {
return rsConfigBuilder.build();
}
/**
* Creates the configuration to use for {@link TransportBuilder}.
*
* @return config
*/
protected TransportConfig buildTransportConfig() {
return transportConfigBuilder.build();
}
protected boolean isWeakReference() {
return weakReference;
}
protected byte getEndianess() {
return endianess;
}
protected IDisconnectCallback getDisconnectCallback() {
return disconnectCallback;
}
/**
* Returns the builder to configure the receiving thread pools.
* @return builder
*/
public ReceivingServiceConfigBuilder<R> receivingThreadConfig() {
return rsConfigBuilder;
}
/**
* Returns the builder to configure the used transport.
* @return builder
*/
public TransportConfigBuilder<?, R> transportConfig() {
return transportConfigBuilder;
}
/**
* Set the size of the thread-pool used to handle signals from the bus.
* Caution: Using thread-pool size &gt; 1 may cause signals to be handled out-of-order
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
* @deprecated use receivingThreadConfig().withSignalThreadCount(_threads)
*/
@Deprecated(since = "4.2.0", forRemoval = true)
public R withSignalThreadCount(int _threads) {
receivingThreadConfig().withSignalThreadCount(_threads);
return self();
}
/**
* Set the size of the thread-pool used to handle error messages received on the bus.
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
* @deprecated use receivingThreadConfig().withErrorHandlerThreadCount(_threads)
*/
@Deprecated(since = "4.2.0", forRemoval = true)
public R withErrorHandlerThreadCount(int _threads) {
receivingThreadConfig().withErrorHandlerThreadCount(_threads);
return self();
}
/**
* Set the size of the thread-pool used to handle methods calls previously sent to the bus.
* The thread pool size has to be &gt; 1 to handle recursive calls.
* <p>
* Default: 4
*
* @param _threads int &gt;= 1
* @return this
* @deprecated use receivingThreadConfig().withMethodCallThreadCount(_threads)
*/
@Deprecated(since = "4.2.0", forRemoval = true)
public R withMethodCallThreadCount(int _threads) {
receivingThreadConfig().withMethodCallThreadCount(_threads);
return self();
}
/**
* Set the size of the thread-pool used to handle method return values received on the bus.
* <p>
* Default: 1
*
* @param _threads int &gt;= 1
* @return this
* @deprecated use receivingThreadConfig().withMethodReturnThreadCount(_threads)
*/
@Deprecated(since = "4.2.0", forRemoval = true)
public R withMethodReturnThreadCount(int _threads) {
receivingThreadConfig().withMethodReturnThreadCount(_threads);
return self();
}
/**
* Set the endianess for the connection
* Default is based on system endianess.
*
* @param _endianess {@link Endian#BIG} or {@value Endian#LITTLE}
* @return this
*/
public R withEndianess(byte _endianess) {
if (_endianess == Endian.BIG || _endianess == Endian.LITTLE) {
endianess = _endianess;
}
return self();
}
/**
* Enable/Disable weak references on connection.
* Default is false.
*
* @param _weakRef true to enable
* @return this
*/
public R withWeakReferences(boolean _weakRef) {
weakReference = _weakRef;
return self();
}
/**
* Set the given disconnect callback to the created connection.
*
* @param _disconnectCallback callback
* @return this
*/
public R withDisconnectCallback(IDisconnectCallback _disconnectCallback) {
disconnectCallback = _disconnectCallback;
return self();
}
public abstract C build() throws DBusException;
/**
* Get the default system endianness.
*
* @return LITTLE or BIG
*/
public static byte getSystemEndianness() {
return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)
? Message.Endian.BIG
: Message.Endian.LITTLE;
}
}

View file

@ -0,0 +1,999 @@
package org.freedesktop.dbus.connections.impl;
import static org.freedesktop.dbus.utils.CommonRegexPattern.DBUS_IFACE_PATTERN;
import static org.freedesktop.dbus.utils.CommonRegexPattern.IFACE_PATTERN;
import static org.freedesktop.dbus.utils.CommonRegexPattern.PROXY_SPLIT_PATTERN;
import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.RemoteInvocationHandler;
import org.freedesktop.dbus.RemoteObject;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.IDisconnectAction;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.NotConnected;
import org.freedesktop.dbus.interfaces.DBus;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.DBusSigHandler;
import org.freedesktop.dbus.interfaces.Introspectable;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.ExportedObject;
import org.freedesktop.dbus.types.UInt32;
import org.freedesktop.dbus.utils.AddressBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* Handles a connection to DBus.
* <p>
* This is a Singleton class, only 1 connection to the SYSTEM or SESSION busses can be made. Repeated calls to
* getConnection will return the same reference.
* </p>
* <p>
* Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency
* issues.
* </p>
*/
public final class DBusConnection extends AbstractConnection {
static final ConcurrentMap<String, DBusConnection> CONNECTIONS = new ConcurrentHashMap<>();
private static String dbusMachineIdFile;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<String> busnames;
private final String machineId;
private DBus dbus;
/** Count how many 'connections' we manage internally.
* This is required because a {@link DBusConnection} to the same address will always return the same object and
* the 'real' disconnection should only occur when there is no second/third/whatever connection is left. */
//CHECKSTYLE:OFF
final AtomicInteger concurrentConnections = new AtomicInteger(1);
//CHECKSTYLE:ON
/**
* Whether this connection is used in shared mode.
*/
private final boolean shared;
DBusConnection(boolean _shared, String _machineId, TransportConfig _tranportCfg, ReceivingServiceConfig _rsCfg) throws DBusException {
super(_tranportCfg, _rsCfg);
busnames = new ArrayList<>();
machineId = _machineId;
shared = _shared;
}
/**
* Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. Will
* always register our own session to Dbus.
*
* @param _address The address of the bus to connect to
* @throws DBusException If there is a problem connecting to the Bus.
* @return {@link DBusConnection}
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection getConnection(String _address) throws DBusException {
return getConnection(_address, true, true, AbstractConnection.TCP_CONNECT_TIMEOUT);
}
/**
* Connect to the BUS. If a connection already exists to the specified Bus and the shared-flag is true, a reference is returned.
* Will register our own session to DBus if registerSelf is true (default).
* A new connection is created every time if shared-flag is false.
*
* @param _address The address of the bus to connect to
* @param _registerSelf register own session in dbus
* @param _shared use a shared connections
* @throws DBusException If there is a problem connecting to the Bus.
* @return {@link DBusConnection}
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection getConnection(String _address, boolean _registerSelf, boolean _shared)
throws DBusException {
return getConnection(_address, _registerSelf, _shared, AbstractConnection.TCP_CONNECT_TIMEOUT);
}
/**
* Connect to the BUS. If a connection already exists to the specified Bus and the shared-flag is true, a reference is returned.
* Will register our own session to DBus if registerSelf is true (default).
* A new connection is created every time if shared-flag is false.
*
* @param _address The address of the bus to connect to
* @param _registerSelf register own session in dbus
* @param _shared use a shared connections
* @param _timeout connect timeout if this is a TCP socket, 0 will block forever, if this is not a TCP socket this value is ignored
* @throws DBusException If there is a problem connecting to the Bus.
* @return {@link DBusConnection}
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection getConnection(String _address, boolean _registerSelf, boolean _shared, int _timeout)
throws DBusException {
return DBusConnectionBuilder.forAddress(_address)
.withRegisterSelf(_registerSelf)
.withShared(_shared)
.transportConfig()
.withAdditionalConfig("TIMEOUT", 10000)
.back()
.build();
}
/**
* Connect to DBus.
* If a connection already exists to the specified Bus, a reference to it is returned.
*
* @param _bustype The Bus to connect to.
* @return {@link DBusConnection}
*
* @throws DBusException If there is a problem connecting to the Bus.
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection getConnection(DBusBusType _bustype) throws DBusException {
return getConnection(_bustype, true, AbstractConnection.TCP_CONNECT_TIMEOUT);
}
/**
* Connect to DBus using a new connection even if there is already a connection established.
*
* @param _bustype The Bus to connect to.
*
* @return {@link DBusConnection}
*
* @throws DBusException If there is a problem connecting to the Bus.
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection newConnection(DBusBusType _bustype) throws DBusException {
return getConnection(_bustype, false, AbstractConnection.TCP_CONNECT_TIMEOUT);
}
/**
* Connect to the BUS.
* If a connection to the specified Bus already exists and shared-flag is true, a reference to it is returned.
* Otherwise a new connection will be created.
*
* @param _bustype The Bus to connect to.
* @param _shared use shared connection
* @param _timeout connect timeout if this is a TCP socket, 0 will block forever, if this is not a TCP socket this value is ignored
*
* @return {@link DBusConnection}
*
* @throws DBusException If there is a problem connecting to the Bus.
* @deprecated use {@link DBusConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static DBusConnection getConnection(DBusBusType _bustype, boolean _shared, int _timeout) throws DBusException {
BusAddress address;
switch (_bustype) {
case SYSTEM:
address = AddressBuilder.getSystemConnection();
break;
case SESSION:
address = AddressBuilder.getSessionConnection(dbusMachineIdFile);
break;
default:
throw new DBusException("Invalid Bus Type: " + _bustype);
}
if (!TransportBuilder.getRegisteredBusTypes().contains("UNIX") // no unix transport
&& TransportBuilder.getRegisteredBusTypes().contains("TCP") // but tcp transport
&& (address == null || address.isBusType("UNIX"))) { // no address or unix socket address
// no UNIX transport available, or lookup did not return anything useful
address = BusAddress.of(System.getProperty(TCP_ADDRESS_PROPERTY));
}
return getConnection(address.toString(), true, _shared, _timeout);
}
private AtomicInteger getConcurrentConnections() {
return concurrentConnections;
}
/**
* Set a specific machine-id file path to read the machine ID from. The
* system variable DBUS_MACHINE_ID_LOCATION will take precedence
* over this.
*
* @param _dbusMachineIdFile file containing DBus machine ID.
* @deprecated no longer required when {@link DBusConnectionBuilder} is used
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public static void setDbusMachineIdFile(String _dbusMachineIdFile) {
DBusConnection.dbusMachineIdFile = _dbusMachineIdFile;
}
/**
* Connect to bus and register if asked.
* Should only be called by Builder.
*
* @param _registerSelf true to register
* @throws DBusException if registering fails
*/
void connect(boolean _registerSelf) throws DBusException {
// start listening for calls
listen();
// register disconnect handlers
DBusSigHandler<?> h = new SigHandler();
addSigHandlerWithoutMatch(DBus.NameAcquired.class, h);
// register ourselves if not disabled
if (_registerSelf) {
dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class);
try {
busnames.add(dbus.Hello());
} catch (DBusExecutionException _ex) {
logger.debug("Error while doing 'Hello' handshake", _ex);
throw new DBusException(_ex.getMessage());
}
}
}
/**
* Tries to resolve a proxy to a remote object.
* If a type class is given, it tries to convert the object using that class.
* If null is given as type, it tries to find a proper interface for this object.
*
* @param <T> object type (DBusInterface compatible)
* @param _source source
* @param _path path
* @param _type class of object type
*
* @return DBusInterface compatible object
*
* @throws DBusException when something goes wrong
*
* @apiNote This method is only intended for internal use.
* Visibility may change in future release
*/
@SuppressWarnings("unchecked")
public <T extends DBusInterface> T dynamicProxy(String _source, String _path, Class<T> _type) throws DBusException {
logger.debug("Introspecting {} on {} for dynamic proxy creation", _path, _source);
try {
Introspectable intro = getRemoteObject(_source, _path, Introspectable.class);
String data = intro.Introspect();
logger.trace("Got introspection data: {}", data);
String[] tags = PROXY_SPLIT_PATTERN.split(data);
List<String> ifaces = Arrays.stream(tags).filter(t -> t.startsWith("interface"))
.map(t -> IFACE_PATTERN.matcher(t).replaceAll("$1"))
.map(i -> {
if (i.startsWith("org.freedesktop.DBus.")) { // if this is a default DBus interface, look for it in our package structure
return DBUS_IFACE_PATTERN.matcher(i).replaceAll("$1");
}
return i;
})
.collect(Collectors.toList());
List<Class<?>> ifcs = findMatchingTypes(_type, ifaces);
// interface could not be found, we guess that this exported object at least support DBusInterface
if (ifcs.isEmpty()) {
ifcs.add(DBusInterface.class);
}
RemoteObject ro = new RemoteObject(_source, _path, _type, false);
DBusInterface newi = (DBusInterface) Proxy.newProxyInstance(ifcs.get(0).getClassLoader(),
ifcs.toArray(Class[]::new), new RemoteInvocationHandler(this, ro));
getImportedObjects().put(newi, ro);
return (T) newi;
} catch (Exception _ex) {
logger.debug("", _ex);
throw new DBusException(
String.format("Failed to create proxy object for %s exported by %s. Reason: %s", _path,
_source, _ex.getMessage()));
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends DBusInterface> T getExportedObject(String _source, String _path, Class<T> _type) throws DBusException {
ExportedObject o;
synchronized (getExportedObjects()) {
o = getExportedObjects().get(_path);
}
if (null != o && o.getObject().get() == null) {
unExportObject(_path);
o = null;
}
if (null != o) {
return (T) o.getObject().get();
}
if (null == _source) {
throw new DBusException("Not an object exported by this connection and no remote specified");
}
return dynamicProxy(_source, _path, _type);
}
@Override
public DBusInterface getExportedObject(String _source, String _path) throws DBusException {
return getExportedObject(_source, _path, null);
}
/**
* Release a bus name. Releases the name so that other people can use it
*
* @param _busname
* The name to release. MUST be in dot-notation like "org.freedesktop.local"
* @throws DBusException
* If the busname is incorrectly formatted.
*/
public void releaseBusName(String _busname) throws DBusException {
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name");
}
try {
dbus.ReleaseName(_busname);
} catch (DBusExecutionException _ex) {
logger.debug("", _ex);
throw new DBusException(_ex.getMessage());
}
synchronized (this.busnames) {
this.busnames.remove(_busname);
}
}
/**
* Request a bus name. Request the well known name that this should respond to on the Bus.
*
* @param _busname
* The name to respond to. MUST be in dot-notation like "org.freedesktop.local"
* @throws DBusException
* If the register name failed, or our name already exists on the bus. or if busname is incorrectly
* formatted.
*/
public void requestBusName(String _busname) throws DBusException {
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name");
}
UInt32 rv;
try {
rv = dbus.RequestName(_busname,
new UInt32(DBus.DBUS_NAME_FLAG_REPLACE_EXISTING | DBus.DBUS_NAME_FLAG_DO_NOT_QUEUE));
} catch (DBusExecutionException _exDb) {
logger.debug("", _exDb);
throw new DBusException(_exDb);
}
switch (rv.intValue()) {
case DBus.DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
case DBus.DBUS_REQUEST_NAME_REPLY_EXISTS:
throw new DBusException("Failed to register bus name");
case DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
case DBus.DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
default:
break;
}
synchronized (this.busnames) {
this.busnames.add(_busname);
}
}
/**
* Returns the unique name of this connection.
*
* @return unique name
*/
public String getUniqueName() {
return busnames.get(0);
}
/**
* Returns all the names owned by this connection.
*
* @return connection names
*/
public String[] getNames() {
Set<String> names = new TreeSet<>();
names.addAll(busnames);
return names.toArray(String[]::new);
}
public <I extends DBusInterface> I getPeerRemoteObject(String _busname, String _objectpath, Class<I> _type)
throws DBusException {
return getPeerRemoteObject(_busname, _objectpath, _type, true);
}
/**
* Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus
* name when you call it. This means that if a well known name is released by one process and acquired by another
* calls to objects gained from this method will continue to operate on the original process.
*
* This method will use bus introspection to determine the interfaces on a remote object and so <b>may block</b> and
* <b>may fail</b>. The resulting proxy object will, however, be castable to any interface it implements. It will
* also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct
* method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd
* happens, try specifying the interface explicitly.
*
* @param _busname
* The bus name to connect to. Usually a well known bus name in dot-notation (such as
* "org.freedesktop.local") or may be a DBus address such as ":1-16".
* @param _objectpath
* The path on which the process is exporting the object.$
* @return A reference to a remote object.
* @throws ClassCastException
* If type is not a sub-type of DBusInterface
* @throws DBusException
* If busname or objectpath are incorrectly formatted.
*/
public DBusInterface getPeerRemoteObject(String _busname, String _objectpath) throws DBusException {
if (null == _busname) {
throw new DBusException("Invalid bus name: null");
}
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name: " + _busname);
}
String unique = dbus.GetNameOwner(_busname);
return dynamicProxy(unique, _objectpath, null);
}
/**
* Return a reference to a remote object. This method will always refer to the well known name (if given) rather
* than resolving it to a unique bus name. In particular this means that if a process providing the well known name
* disappears and is taken over by another process proxy objects gained by this method will make calls on the new
* proccess.
*
* This method will use bus introspection to determine the interfaces on a remote object and so <b>may block</b> and
* <b>may fail</b>. The resulting proxy object will, however, be castable to any interface it implements. It will
* also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct
* method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd
* happens, try specifying the interface explicitly.
*
* @param _busname
* The bus name to connect to. Usually a well known bus name name in dot-notation (such as
* "org.freedesktop.local") or may be a DBus address such as ":1-16".
* @param _objectpath
* The path on which the process is exporting the object.
* @return A reference to a remote object.
* @throws ClassCastException
* If type is not a sub-type of DBusInterface
* @throws DBusException
* If busname or objectpath are incorrectly formatted.
*/
public DBusInterface getRemoteObject(String _busname, String _objectpath) throws DBusException {
if (null == _busname) {
throw new DBusException("Invalid bus name: null");
}
if (null == _objectpath) {
throw new DBusException("Invalid object path: null");
}
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name: " + _busname);
}
if (_objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectpath).matches()) {
throw new DBusException("Invalid object path: " + _objectpath);
}
return dynamicProxy(_busname, _objectpath, null);
}
/**
* Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus
* name when you call it. This means that if a well known name is released by one process and acquired by another
* calls to objects gained from this method will continue to operate on the original process.
*
* @param <I>
* class extending {@link DBusInterface}
* @param _busname
* The bus name to connect to. Usually a well known bus name in dot-notation (such as
* "org.freedesktop.local") or may be a DBus address such as ":1-16".
* @param _objectpath
* The path on which the process is exporting the object.$
* @param _type
* The interface they are exporting it on. This type must have the same full class name and exposed
* method signatures as the interface the remote object is exporting.
* @param _autostart
* Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when
* calling a method with auto-start enabled, if the destination is a well-known name and is not owned the
* bus will attempt to start a process to take the name. When disabled an error is returned immediately.
* @return A reference to a remote object.
* @throws ClassCastException
* If type is not a sub-type of DBusInterface
* @throws DBusException
* If busname or objectpath are incorrectly formatted or type is not in a package.
*/
public <I extends DBusInterface> I getPeerRemoteObject(String _busname, String _objectpath, Class<I> _type,
boolean _autostart) throws DBusException {
if (null == _busname) {
throw new DBusException("Invalid bus name: null");
}
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name: " + _busname);
}
String unique = dbus.GetNameOwner(_busname);
return getRemoteObject(unique, _objectpath, _type, _autostart);
}
/**
* Return a reference to a remote object. This method will always refer to the well known name (if given) rather
* than resolving it to a unique bus name. In particular this means that if a process providing the well known name
* disappears and is taken over by another process proxy objects gained by this method will make calls on the new
* proccess.
*
* @param <I>
* class extending {@link DBusInterface}
* @param _busname
* The bus name to connect to. Usually a well known bus name name in dot-notation (such as
* "org.freedesktop.local") or may be a DBus address such as ":1-16".
* @param _objectpath
* The path on which the process is exporting the object.
* @param _type
* The interface they are exporting it on. This type must have the same full class name and exposed
* method signatures as the interface the remote object is exporting.
* @return A reference to a remote object.
* @throws ClassCastException
* If type is not a sub-type of DBusInterface
* @throws DBusException
* If busname or objectpath are incorrectly formatted or type is not in a package.
*/
public <I extends DBusInterface> I getRemoteObject(String _busname, String _objectpath, Class<I> _type)
throws DBusException {
return getRemoteObject(_busname, _objectpath, _type, true);
}
/**
* Return a reference to a remote object. This method will always refer to the well known name (if given) rather
* than resolving it to a unique bus name. In particular this means that if a process providing the well known name
* disappears and is taken over by another process proxy objects gained by this method will make calls on the new
* proccess.
*
* @param <I>
* class extending {@link DBusInterface}
* @param _busname
* The bus name to connect to. Usually a well known bus name name in dot-notation (such as
* "org.freedesktop.local") or may be a DBus address such as ":1-16".
* @param _objectpath
* The path on which the process is exporting the object.
* @param _type
* The interface they are exporting it on. This type must have the same full class name and exposed
* method signatures as the interface the remote object is exporting.
* @param _autostart
* Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when
* calling a method with auto-start enabled, if the destination is a well-known name and is not owned the
* bus will attempt to start a process to take the name. When disabled an error is returned immediately.
* @return A reference to a remote object.
* @throws ClassCastException
* If type is not a sub-type of DBusInterface
* @throws DBusException
* If busname or objectpath are incorrectly formatted or type is not in a package.
*/
@SuppressWarnings("unchecked")
public <I extends DBusInterface> I getRemoteObject(String _busname, String _objectpath, Class<I> _type,
boolean _autostart) throws DBusException {
if (null == _busname) {
throw new DBusException("Invalid bus name: null");
}
if (null == _objectpath) {
throw new DBusException("Invalid object path: null");
}
if (null == _type) {
throw new ClassCastException("Not A DBus Interface");
}
if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) {
throw new DBusException("Invalid bus name: " + _busname);
}
if (!OBJECT_REGEX_PATTERN.matcher(_objectpath).matches() || _objectpath.length() > MAX_NAME_LENGTH) {
throw new DBusException("Invalid object path: " + _objectpath);
}
if (!DBusInterface.class.isAssignableFrom(_type)) {
throw new ClassCastException("Not A DBus Interface");
}
// don't let people import things which don't have a
// valid D-Bus interface name
if (_type.getName().equals(_type.getSimpleName())) {
throw new DBusException("DBusInterfaces cannot be declared outside a package");
}
RemoteObject ro = new RemoteObject(_busname, _objectpath, _type, _autostart);
I i = (I) Proxy.newProxyInstance(_type.getClassLoader(), new Class[] {
_type
}, new RemoteInvocationHandler(this, ro));
getImportedObjects().put(i, ro);
return i;
}
/**
* Remove a Signal Handler. Stops listening for this signal.
*
* @param <T>
* class extending {@link DBusSignal}
* @param _type
* The signal to watch for.
* @param _source
* The source of the signal.
* @param _handler
* the handler
* @throws DBusException
* If listening for the signal on the bus failed.
* @throws ClassCastException
* If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> void removeSigHandler(Class<T> _type, String _source, DBusSigHandler<T> _handler)
throws DBusException {
validateSignal(_type, _source);
removeSigHandler(new DBusMatchRule(_type, _source, null), _handler);
}
/**
* Remove a Signal Handler. Stops listening for this signal.
*
* @param <T>
* class extending {@link DBusSignal}
* @param _type
* The signal to watch for.
* @param _source
* The source of the signal.
* @param _object
* The object emitting the signal.
* @param _handler
* the handler
* @throws DBusException
* If listening for the signal on the bus failed.
* @throws ClassCastException
* If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> void removeSigHandler(Class<T> _type, String _source, DBusInterface _object,
DBusSigHandler<T> _handler) throws DBusException {
validateSignal(_type, _source);
String objectpath = getImportedObjects().get(_object).getObjectPath();
if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) {
throw new DBusException("Invalid object path: " + objectpath);
}
removeSigHandler(new DBusMatchRule(_type, _source, objectpath), _handler);
}
/**
* {@inheritDoc}
*/
@Override
protected <T extends DBusSignal> void removeSigHandler(DBusMatchRule _rule, DBusSigHandler<T> _handler)
throws DBusException {
Queue<DBusSigHandler<? extends DBusSignal>> dbusSignalList = getHandledSignals().get(_rule);
if (null != dbusSignalList) {
dbusSignalList.remove(_handler);
if (dbusSignalList.isEmpty()) {
getHandledSignals().remove(_rule);
try {
dbus.RemoveMatch(_rule.toString());
} catch (NotConnected _ex) {
logger.debug("No connection.", _ex);
} catch (DBusExecutionException _ex) {
logger.debug("", _ex);
throw new DBusException(_ex);
}
}
}
}
/**
* Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type,
* name and source.
*
* @param <T>
* class extending {@link DBusSignal}
* @param _type
* The signal to watch for.
* @param _source
* The process which will send the signal. This <b>MUST</b> be a unique bus name and not a well known
* name.
* @return closeable that removes signal handler
* @param _handler
* The handler to call when a signal is received.
* @throws DBusException
* If listening for the signal on the bus failed.
* @throws ClassCastException
* If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> AutoCloseable addSigHandler(Class<T> _type, String _source, DBusSigHandler<T> _handler)
throws DBusException {
validateSignal(_type, _source);
addSigHandler(new DBusMatchRule(_type, _source, null), (DBusSigHandler<? extends DBusSignal>) _handler);
return new AutoCloseable() {
@Override
public void close() throws DBusException {
removeSigHandler(_type, _source, _handler);
}
};
}
/**
* Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type,
* name, source and object.
*
* @param <T>
* class extending {@link DBusSignal}
* @param _type
* The signal to watch for.
* @param _source
* The process which will send the signal. This <b>MUST</b> be a unique bus name and not a well known
* name.
* @param _object
* The object from which the signal will be emitted
* @param _handler
* The handler to call when a signal is received.
* @return closeable that removes signal handler
* @throws DBusException
* If listening for the signal on the bus failed.
* @throws ClassCastException
* If type is not a sub-type of DBusSignal.
*/
public <T extends DBusSignal> AutoCloseable addSigHandler(Class<T> _type, String _source, DBusInterface _object,
DBusSigHandler<T> _handler) throws DBusException {
validateSignal(_type, _source);
String objectpath = getImportedObjects().get(_object).getObjectPath();
if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) {
throw new DBusException("Invalid object path: " + objectpath);
}
addSigHandler(new DBusMatchRule(_type, _source, objectpath), (DBusSigHandler<? extends DBusSignal>) _handler);
return new AutoCloseable() {
@Override
public void close() throws DBusException {
removeSigHandler(_type, _source, _object, _handler);
}
};
}
/**
* Checks if given type is a DBusSignal and matches the required rules.
*
* @param <T> type of class
* @param _type class
* @param _source
* @throws DBusException when validation fails
*/
private <T extends DBusSignal> void validateSignal(Class<T> _type, String _source) throws DBusException {
if (!DBusSignal.class.isAssignableFrom(_type)) {
throw new ClassCastException("Not A DBus Signal");
}
if (BUSNAME_REGEX.matcher(_source).matches()) {
throw new DBusException(
"Cannot watch for signals based on well known bus name as source, only unique names.");
}
if (_source.length() > MAX_NAME_LENGTH || !CONNID_REGEX.matcher(_source).matches()) {
throw new DBusException("Invalid bus name: " + _source);
}
}
/**
* {@inheritDoc}
*/
@Override
public <T extends DBusSignal> AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler<T> _handler)
throws DBusException {
Objects.requireNonNull(_rule, "Match rule cannot be null");
Objects.requireNonNull(_handler, "Handler cannot be null");
AtomicBoolean addMatch = new AtomicBoolean(false); // flag to perform action if this is a new signal key
Queue<DBusSigHandler<? extends DBusSignal>> dbusSignalList =
getHandledSignals().computeIfAbsent(_rule, v -> {
Queue<DBusSigHandler<? extends DBusSignal>> signalList = new ConcurrentLinkedQueue<>();
addMatch.set(true);
return signalList;
});
// add handler to signal list
dbusSignalList.add(_handler);
// add match rule if this rule is new
if (addMatch.get()) {
try {
dbus.AddMatch(_rule.toString());
} catch (DBusExecutionException _ex) {
logger.debug("Cannot add match rule: " + _rule.toString(), _ex);
throw new DBusException("Cannot add match rule.", _ex);
}
}
return new AutoCloseable() {
@Override
public void close() throws DBusException {
removeSigHandler(_rule, _handler);
}
};
}
/**
* Disconnect from the Bus.
* If this is a shared connection, it only disconnects when the last reference to the bus has called disconnect.
* If this is not a shared connection, disconnect will close the connection instantly.
*/
@Override
public void disconnect() {
if (!isConnected()) { // already disconnected
return;
}
// if this is a shared connection, keep track of disconnect calls
if (shared) {
synchronized (CONNECTIONS) {
DBusConnection connection = CONNECTIONS.get(getAddress().toString());
if (connection != null) {
if (connection.getConcurrentConnections().get() <= 1) { // one left, this should be ourselves
CONNECTIONS.remove(getAddress().toString());
super.disconnect();
} else {
logger.debug("Still {} connections left, decreasing connection counter", connection.getConcurrentConnections().get() - 1);
Optional.ofNullable(getDisconnectCallback()).ifPresent(cb -> cb.requestedDisconnect(connection.getConcurrentConnections().get()));
connection.getConcurrentConnections().decrementAndGet();
}
}
}
} else { // this is a standalone non-shared session, disconnect directly using super's implementation
IDisconnectAction beforeDisconnectAction = () -> {
// get all busnames from the list which matches the usual pattern
// this is required as the list also contains internal names like ":1.11"
// it is also required to put the results in a new list, otherwise we would get a
// concurrent modification exception later (calling releaseBusName() will modify the busnames List)
synchronized (busnames) {
List<String> lBusNames = busnames.stream()
.filter(busName -> busName != null && !(busName.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(busName).matches()))
.collect(Collectors.toList());
lBusNames.forEach(busName -> {
try {
releaseBusName(busName);
} catch (DBusException _ex) {
logger.error("Error while releasing busName '" + busName + "'.", _ex);
}
});
}
// remove all exported objects before disconnecting
Map<String, ExportedObject> exportedObjects = getExportedObjects();
synchronized (exportedObjects) {
List<String> exportedKeys = exportedObjects.keySet().stream().filter(f -> f != null).collect(Collectors.toList());
for (String key : exportedKeys) {
unExportObject(key);
}
}
};
super.disconnect(beforeDisconnectAction, null);
}
}
/**
* Same as disconnect.
*/
@Override
public void close() throws IOException {
disconnect();
}
@Override
public String getMachineId() {
return machineId;
}
@Override
public void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler<DBusSignal> _handler) throws DBusException {
Queue<DBusSigHandler<DBusSignal>> genericSignalsList = getGenericHandledSignals().get(_rule);
if (null != genericSignalsList) {
genericSignalsList.remove(_handler);
if (genericSignalsList.isEmpty()) {
getGenericHandledSignals().remove(_rule);
try {
dbus.RemoveMatch(_rule.toString());
} catch (NotConnected _ex) {
logger.debug("No connection.", _ex);
} catch (DBusExecutionException _ex) {
logger.debug("", _ex);
throw new DBusException(_ex);
}
}
}
}
@Override
public AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler<DBusSignal> _handler) throws DBusException {
AtomicBoolean addMatch = new AtomicBoolean(false); // flag to perform action if this is a new signal key
Queue<DBusSigHandler<DBusSignal>> genericSignalsList =
getGenericHandledSignals().computeIfAbsent(_rule, v -> {
Queue<DBusSigHandler<DBusSignal>> signalsList = new ConcurrentLinkedQueue<>();
addMatch.set(true);
return signalsList;
});
genericSignalsList.add(_handler);
if (addMatch.get()) {
try {
dbus.AddMatch(_rule.toString());
} catch (DBusExecutionException _ex) {
logger.debug("", _ex);
throw new DBusException(_ex.getMessage());
}
}
return new AutoCloseable() {
@Override
public void close() throws DBusException {
removeGenericSigHandler(_rule, _handler);
}
};
}
private class SigHandler implements DBusSigHandler<DBusSignal> {
@Override
public void handle(DBusSignal _signal) {
if (_signal instanceof DBus.NameAcquired) {
synchronized (busnames) {
busnames.add(((DBus.NameAcquired) _signal).name);
}
}
}
}
public enum DBusBusType {
/**
* System Bus
*/
SYSTEM,
/**
* Session Bus
*/
SESSION;
}
}

View file

@ -0,0 +1,222 @@
package org.freedesktop.dbus.connections.impl;
import static org.freedesktop.dbus.utils.AddressBuilder.getDbusMachineId;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.connections.impl.DBusConnection.DBusBusType;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.exceptions.AddressResolvingException;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.utils.AddressBuilder;
import java.nio.ByteOrder;
/**
* Builder to create a new DBusConnection.
*
* @author hypfvieh
* @version 4.1.0 - 2022-02-04
*/
public final class DBusConnectionBuilder extends BaseConnectionBuilder<DBusConnectionBuilder, DBusConnection> {
private final String machineId;
private boolean registerSelf = true;
private boolean shared = true;
private DBusConnectionBuilder(BusAddress _address, String _machineId) {
super(DBusConnectionBuilder.class, _address);
machineId = _machineId;
}
/**
* Create a new default connection connecting to DBus session bus but use an alternative input for the machineID.
*
* @param _machineIdFileLocation file with machine ID
*
* @return {@link DBusConnectionBuilder}
*/
public static DBusConnectionBuilder forSessionBus(String _machineIdFileLocation) {
BusAddress address = AddressBuilder.getSessionConnection(_machineIdFileLocation);
address = validateTransportAddress(address);
DBusConnectionBuilder instance = new DBusConnectionBuilder(address, getDbusMachineId(_machineIdFileLocation));
return instance;
}
/**
* Create new default connection to the DBus system bus.
*
* @return {@link DBusConnectionBuilder}
*/
public static DBusConnectionBuilder forSystemBus() {
BusAddress address = AddressBuilder.getSystemConnection();
address = validateTransportAddress(address);
return new DBusConnectionBuilder(address, getDbusMachineId(null));
}
/**
* Create a new default connection connecting to the DBus session bus.
*
* @return {@link DBusConnectionBuilder}
*/
public static DBusConnectionBuilder forSessionBus() {
return forSessionBus(null);
}
/**
* Create a default connection to DBus using the given bus type.
*
* @param _type bus type
*
* @return this
*/
public static DBusConnectionBuilder forType(DBusBusType _type) {
return forType(_type, null);
}
/**
* Create a default connection to DBus using the given bus type and machineIdFile.
*
* @param _type bus type
* @param _machineIdFile machineId file
*
* @return this
*/
public static DBusConnectionBuilder forType(DBusBusType _type, String _machineIdFile) {
if (_type == DBusBusType.SESSION) {
return forSessionBus(_machineIdFile);
} else if (_type == DBusBusType.SYSTEM) {
return forSystemBus();
}
throw new IllegalArgumentException("Unknown bus type: " + _type);
}
/**
* Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons).
*
* @param _address address to use
* @return this
*/
public static DBusConnectionBuilder forAddress(String _address) {
DBusConnectionBuilder instance = new DBusConnectionBuilder(BusAddress.of(_address), getDbusMachineId(null));
return instance;
}
/**
* Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons).
*
* @param _address address to use
* @return this
*
* @since 4.2.0 - 2022-07-18
*/
public static DBusConnectionBuilder forAddress(BusAddress _address) {
DBusConnectionBuilder instance = new DBusConnectionBuilder(_address, getDbusMachineId(null));
return instance;
}
/**
* Checks if the given address can be used with the available transports.
* Will fallback to TCP if no address given and TCP transport is available.
*
* @param _address address to check
* @return address, maybe fallback address
*/
private static BusAddress validateTransportAddress(BusAddress _address) {
if (TransportBuilder.getRegisteredBusTypes().isEmpty()) {
throw new IllegalArgumentException("No transports found to connect to DBus. Please add at least one transport provider to your classpath");
}
BusAddress address = _address;
// no unix transport but address wants to use a unix socket
if (!TransportBuilder.getRegisteredBusTypes().contains("UNIX")
&& address != null
&& address.isBusType("UNIX")) {
throw new AddressResolvingException("No transports found to handle UNIX socket connections. Please add a unix-socket transport provider to your classpath");
}
// no tcp transport but TCP address given
if (!TransportBuilder.getRegisteredBusTypes().contains("TCP")
&& address != null
&& address.isBusType("TCP")) {
throw new AddressResolvingException("No transports found to handle TCP connections. Please add a TCP transport provider to your classpath");
}
return address;
}
/**
* Register the new connection on DBus using 'hello' message. Default is true.
*
* @param _register boolean
* @return this
*/
public DBusConnectionBuilder withRegisterSelf(boolean _register) {
registerSelf = _register;
return this;
}
/**
* Use this connection as shared connection. Shared connection means that the same connection is used multiple times
* if the connection parameter did not change. Default is true.
*
* @param _shared boolean
* @return this
*/
public DBusConnectionBuilder withShared(boolean _shared) {
shared = _shared;
return this;
}
/**
* Create the new {@link DBusConnection}.
*
* @return {@link DBusConnection}
* @throws DBusException when DBusConnection could not be opened
*/
@Override
public DBusConnection build() throws DBusException {
ReceivingServiceConfig cfg = buildThreadConfig();
TransportConfig transportCfg = buildTransportConfig();
DBusConnection c;
if (shared) {
synchronized (DBusConnection.CONNECTIONS) {
String busAddressStr = transportCfg.getBusAddress().toString();
c = DBusConnection.CONNECTIONS.get(busAddressStr);
if (c != null) {
c.concurrentConnections.incrementAndGet();
return c; // this connection already exists, do not change anything
} else {
c = new DBusConnection(shared, machineId, transportCfg, cfg);
DBusConnection.CONNECTIONS.put(busAddressStr, c);
}
}
} else {
c = new DBusConnection(shared, machineId, transportCfg, cfg);
}
c.setDisconnectCallback(getDisconnectCallback());
c.setWeakReferences(isWeakReference());
DBusConnection.setEndianness(getEndianess());
c.connect(registerSelf);
return c;
}
/**
* Get the default system endianness.
*
* @return LITTLE or BIG
* @deprecated if required, use {@link BaseConnectionBuilder#getSystemEndianness()}
*/
@Deprecated(forRemoval = true, since = "4.2.0")
public static byte getSystemEndianness() {
return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)
? Message.Endian.BIG
: Message.Endian.LITTLE;
}
}

View file

@ -0,0 +1,300 @@
package org.freedesktop.dbus.connections.impl;
import static org.freedesktop.dbus.utils.CommonRegexPattern.IFACE_PATTERN;
import static org.freedesktop.dbus.utils.CommonRegexPattern.PROXY_SPLIT_PATTERN;
import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.RemoteInvocationHandler;
import org.freedesktop.dbus.RemoteObject;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.DBusSigHandler;
import org.freedesktop.dbus.interfaces.Introspectable;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.ExportedObject;
import org.freedesktop.dbus.utils.Hexdump;
import org.freedesktop.dbus.utils.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;
/**
* Handles a peer to peer connection between two applications without a bus daemon.
* <p>
* Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues.
* </p>
*/
public class DirectConnection extends AbstractConnection {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final String machineId;
/**
* Create a direct connection to another application.
* @param _address The address to connect to. This is a standard D-Bus address, except that the additional parameter 'listen=true' should be added in the application which is creating the socket.
* @throws DBusException on error
* @deprecated use {@link DirectConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public DirectConnection(String _address) throws DBusException {
this(_address, AbstractConnection.TCP_CONNECT_TIMEOUT);
}
/**
* Create a direct connection to another application.
* @param _address The address to connect to. This is a standard D-Bus address, except that the additional parameter 'listen=true' should be added in the application which is creating the socket.
* @param _timeout the timeout set for the underlying socket. 0 will block forever on the underlying socket.
* @throws DBusException on error
* @deprecated use {@link DirectConnectionBuilder}
*/
@Deprecated(since = "4.1.0", forRemoval = true)
public DirectConnection(String _address, int _timeout) throws DBusException {
this(createTransportConfig(_address, _timeout), null);
}
DirectConnection(TransportConfig _transportCfg, ReceivingServiceConfig _rsCfg) throws DBusException {
super(_transportCfg, _rsCfg);
machineId = createMachineId();
if (!getAddress().isServer()) {
super.listen();
}
}
@Deprecated(since = "4.2.0", forRemoval = true)
static TransportConfig createTransportConfig(String _address, int _timeout) {
TransportConfig cfg = new TransportConfig();
cfg.setBusAddress(BusAddress.of(_address));
cfg.getAdditionalConfig().put("TIMEOUT", _timeout);
return cfg;
}
/**
* Use this method when running on server side.
* Call will block.
*/
@Override
public void listen() {
if (getAddress().isServer()) {
super.listen();
}
}
private String createMachineId() {
String ascii;
try {
ascii = Hexdump.toAscii(MessageDigest.getInstance("MD5").digest(InetAddress.getLocalHost().getHostName().getBytes()));
return ascii;
} catch (NoSuchAlgorithmException _ex) {
logger.trace("MD5 algorithm not present", _ex);
} catch (UnknownHostException _ex) {
logger.trace("Unable to determine this machines hostname", _ex);
}
return Util.randomString(32);
}
@SuppressWarnings("unchecked")
<T extends DBusInterface> T dynamicProxy(String _path, Class<T> _type) throws DBusException {
try {
Introspectable intro = getRemoteObject(_path, Introspectable.class);
String data = intro.Introspect();
String[] tags = PROXY_SPLIT_PATTERN.split(data);
List<String> ifaces = Arrays.stream(tags).filter(t -> t.startsWith("interface"))
.map(t -> IFACE_PATTERN.matcher(t).replaceAll("$1"))
.collect(Collectors.toList());
List<Class<?>> ifcs = findMatchingTypes(_type, ifaces);
if (ifcs.isEmpty()) {
throw new DBusException("Could not find an interface to cast to");
}
RemoteObject ro = new RemoteObject(null, _path, _type, false);
DBusInterface newi = (DBusInterface) Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), ifcs.toArray(new Class[0]), new RemoteInvocationHandler(this, ro));
getImportedObjects().put(newi, ro);
return (T) newi;
} catch (Exception _ex) {
logger.debug("", _ex);
throw new DBusException(String.format("Failed to create proxy object for %s; reason: %s.", _path, _ex.getMessage()));
}
}
@SuppressWarnings("unchecked")
<T extends DBusInterface> T getExportedObject(String _path, Class<T> _type) throws DBusException {
ExportedObject o = null;
synchronized (getExportedObjects()) {
o = getExportedObjects().get(_path);
}
if (null != o && null == o.getObject().get()) {
unExportObject(_path);
o = null;
}
if (null != o) {
return (T) o.getObject().get();
}
return dynamicProxy(_path, _type);
}
/**
* Return a reference to a remote object.
* This method will always refer to the well known name (if given) rather than resolving it to a unique bus name.
* In particular this means that if a process providing the well known name disappears and is taken over by another process
* proxy objects gained by this method will make calls on the new proccess.
*
* This method will use bus introspection to determine the interfaces on a remote object and so
* <b>may block</b> and <b>may fail</b>. The resulting proxy object will, however, be castable
* to any interface it implements. It will also autostart the process if applicable. Also note
* that the resulting proxy may fail to execute the correct method with overloaded methods
* and that complex types may fail in interesting ways. Basically, if something odd happens,
* try specifying the interface explicitly.
*
* @param _objectPath The path on which the process is exporting the object.
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted.
*/
public DBusInterface getRemoteObject(String _objectPath) throws DBusException {
if (null == _objectPath) {
throw new DBusException("Invalid object path: null");
}
if (_objectPath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectPath).matches()) {
throw new DBusException("Invalid object path: " + _objectPath);
}
return dynamicProxy(_objectPath, null);
}
/**
* Return a reference to a remote object.
* This method will always refer to the well known name (if given) rather than resolving it to a unique bus name.
* In particular this means that if a process providing the well known name disappears and is taken over by another process
* proxy objects gained by this method will make calls on the new proccess.
* @param _objectPath The path on which the process is exporting the object.
* @param _type The interface they are exporting it on. This type must have the same full class name and exposed method signatures
* as the interface the remote object is exporting.
* @param <T> class which extends DBusInterface
* @return A reference to a remote object.
* @throws ClassCastException If type is not a sub-type of DBusInterface
* @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package.
*/
public <T extends DBusInterface> T getRemoteObject(String _objectPath, Class<T> _type) throws DBusException {
if (null == _objectPath) {
throw new DBusException("Invalid object path: null");
}
if (null == _type) {
throw new ClassCastException("Not A DBus Interface");
}
if (_objectPath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectPath).matches()) {
throw new DBusException("Invalid object path: " + _objectPath);
}
if (!DBusInterface.class.isAssignableFrom(_type)) {
throw new ClassCastException("Not A DBus Interface");
}
// don't let people import things which don't have a
// valid D-Bus interface name
if (_type.getName().equals(_type.getSimpleName())) {
throw new DBusException("DBusInterfaces cannot be declared outside a package");
}
RemoteObject ro = new RemoteObject(null, _objectPath, _type, false);
@SuppressWarnings("unchecked")
T i = (T) Proxy.newProxyInstance(_type.getClassLoader(),
new Class[] {_type}, new RemoteInvocationHandler(this, ro));
getImportedObjects().put(i, ro);
return i;
}
@Override
protected <T extends DBusSignal> void removeSigHandler(DBusMatchRule _rule, DBusSigHandler<T> _handler) throws DBusException {
Queue<DBusSigHandler<? extends DBusSignal>> v = getHandledSignals().get(_rule);
if (null != v) {
v.remove(_handler);
if (0 == v.size()) {
getHandledSignals().remove(_rule);
}
}
}
@Override
protected <T extends DBusSignal> AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler<T> _handler) throws DBusException {
Queue<DBusSigHandler<? extends DBusSignal>> v =
getHandledSignals().computeIfAbsent(_rule, val -> {
Queue<DBusSigHandler<? extends DBusSignal>> l = new ConcurrentLinkedQueue<>();
return l;
});
v.add(_handler);
return new AutoCloseable() {
@Override
public void close() throws Exception {
removeSigHandler(_rule, _handler);
}
};
}
@Override
protected void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler<DBusSignal> _handler) throws DBusException {
Queue<DBusSigHandler<DBusSignal>> v = getGenericHandledSignals().get(_rule);
if (null != v) {
v.remove(_handler);
if (0 == v.size()) {
getGenericHandledSignals().remove(_rule);
}
}
}
@Override
protected AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler<DBusSignal> _handler) throws DBusException {
Queue<DBusSigHandler<DBusSignal>> v =
getGenericHandledSignals().computeIfAbsent(_rule, val -> {
Queue<DBusSigHandler<DBusSignal>> l = new ConcurrentLinkedQueue<>();
return l;
});
v.add(_handler);
return new AutoCloseable() {
@Override
public void close() throws Exception {
removeGenericSigHandler(_rule, _handler);
}
};
}
@Override
public <T extends DBusInterface> T getExportedObject(String _source, String _path, Class<T> _type) throws DBusException {
return getExportedObject(_path, _type);
}
@Override
public String getMachineId() {
return machineId;
}
@Override
public DBusInterface getExportedObject(String _source, String _path) throws DBusException {
return getExportedObject(_path, (Class<DBusInterface>) null);
}
}

View file

@ -0,0 +1,64 @@
package org.freedesktop.dbus.connections.impl;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.messages.Message;
import java.nio.ByteOrder;
/**
* Builder to create a new DirectConnection.
*
* @author hypfvieh
* @version 4.1.0 - 2022-02-04
*/
public final class DirectConnectionBuilder extends BaseConnectionBuilder<DirectConnectionBuilder, DirectConnection> {
private DirectConnectionBuilder(BusAddress _address) {
super(DirectConnectionBuilder.class, _address);
}
/**
* Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons).
*
* @param _address address to use
* @return this
*/
public static DirectConnectionBuilder forAddress(String _address) {
DirectConnectionBuilder instance = new DirectConnectionBuilder(BusAddress.of(_address));
return instance;
}
/**
* Create the new {@link DBusConnection}.
*
* @return {@link DBusConnection}
* @throws DBusException when DBusConnection could not be opened
*/
@Override
public DirectConnection build() throws DBusException {
ReceivingServiceConfig rsCfg = buildThreadConfig();
TransportConfig transportCfg = buildTransportConfig();
DirectConnection c = new DirectConnection(transportCfg, rsCfg);
c.setDisconnectCallback(getDisconnectCallback());
c.setWeakReferences(isWeakReference());
DirectConnection.setEndianness(getEndianess());
return c;
}
/**
* Get the default system endianness.
*
* @return LITTLE or BIG
* @deprecated if required, use {@link BaseConnectionBuilder#getSystemEndianness()}
*/
@Deprecated(forRemoval = true, since = "4.2.0")
public static byte getSystemEndianness() {
return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)
? Message.Endian.BIG
: Message.Endian.LITTLE;
}
}

View file

@ -0,0 +1,342 @@
package org.freedesktop.dbus.connections.transports;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.SASL;
import org.freedesktop.dbus.connections.config.SaslConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.exceptions.AuthenticationException;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.InvalidBusAddressException;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.spi.message.IMessageReader;
import org.freedesktop.dbus.spi.message.IMessageWriter;
import org.freedesktop.dbus.spi.message.ISocketProvider;
import org.freedesktop.dbus.spi.message.InputStreamMessageReader;
import org.freedesktop.dbus.spi.message.OutputStreamMessageWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.Objects;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
/**
* Base class for all transport types.
*
* @author hypfvieh
* @since v3.2.0 - 2019-02-08
*/
public abstract class AbstractTransport implements Closeable {
private static final AtomicLong TRANSPORT_ID_GENERATOR = new AtomicLong(0);
private final ServiceLoader<ISocketProvider> spiLoader = ServiceLoader.load(ISocketProvider.class);
private final Logger logger = LoggerFactory.getLogger(getClass());
private final BusAddress address;
private TransportConnection transportConnection;
private boolean fileDescriptorSupported;
private final long transportId = TRANSPORT_ID_GENERATOR.incrementAndGet();
private final TransportConfig config;
protected AbstractTransport(BusAddress _address, TransportConfig _config) {
address = Objects.requireNonNull(_address, "BusAddress required");
config = Objects.requireNonNull(_config, "Config required");
if (_address.isListeningSocket()) {
config.getSaslConfig().setMode(SASL.SaslMode.SERVER);
} else {
config.getSaslConfig().setMode(SASL.SaslMode.CLIENT);
}
config.getSaslConfig().setGuid(address.getGuid());
config.getSaslConfig().setFileDescriptorSupport(hasFileDescriptorSupport());
}
/**
* Write a message to the underlying socket.
*
* @param _msg message to write
* @throws IOException on write error or if output was already closed or null
*/
public void writeMessage(Message _msg) throws IOException {
if (!fileDescriptorSupported && Message.ArgumentType.FILEDESCRIPTOR == _msg.getType()) {
throw new IllegalArgumentException("File descriptors are not supported!");
}
if (transportConnection.getWriter() != null && !transportConnection.getWriter().isClosed()) {
transportConnection.getWriter().writeMessage(_msg);
} else {
throw new IOException("OutputWriter already closed or null");
}
}
/**
* Read a message from the underlying socket.
*
* @return read message, maybe null
* @throws IOException when input already close or null
* @throws DBusException when message could not be converted to a DBus message
*/
public Message readMessage() throws IOException, DBusException {
if (transportConnection.getReader() != null && !transportConnection.getReader().isClosed()) {
return transportConnection.getReader().readMessage();
}
throw new IOException("InputReader already closed or null");
}
/**
* Returns true if inputReader and outputWriter are not yet closed.
* @return boolean
*/
public synchronized boolean isConnected() {
return transportConnection != null
&& transportConnection.getWriter() != null && !transportConnection.getWriter().isClosed()
&& transportConnection.getReader() != null && !transportConnection.getReader().isClosed();
}
/**
* Method to indicate if passing of file descriptors is allowed.
*
* @return true to allow FD passing, false otherwise
*/
protected abstract boolean hasFileDescriptorSupport();
/**
* Return true if the transport supports 'abstract' sockets.
* @return true if abstract sockets supported, false otherwise
*
* @deprecated Is no longer used and will be removed
*/
@Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18")
protected abstract boolean isAbstractAllowed();
/**
* Abstract method implemented by concrete sub classes to establish a connection
* using whatever transport type (e.g. TCP/Unix socket).
* @throws IOException when connection fails
*/
protected abstract SocketChannel connectImpl() throws IOException;
/**
* Establish connection on created transport.<br>
* <p>
* This method can only be used for <b>non-listening</b> connections.<br>
* Trying to use this with listening addresses will throw an {@link InvalidBusAddressException}.
* </p>
*
* @return {@link SocketChannel} of the created connection
* @throws IOException if connection fails
*/
public final SocketChannel connect() throws IOException {
if (getAddress().isListeningSocket()) {
throw new InvalidBusAddressException("Cannot connect when using listening address (try use listen() instead)");
}
transportConnection = internalConnect();
return transportConnection.getChannel();
}
/**
* Start listening on created transport.<br>
* <p>
* This method can only be used for <b>listening</b> connections.<br>
* Trying to use this with non-listening addresses will throw an {@link InvalidBusAddressException}.
* </p>
* <p>
* Will return the {@link TransportConnection} as soon as a client connects.<br>
* Therefore this method should be called in a loop to accept multiple clients
* </p>
*
* @return {@link TransportConnection} containing created {@link SocketChannel} and {@link IMessageReader}/{@link IMessageWriter}
* @throws IOException if connection fails
*/
public final TransportConnection listen() throws IOException {
if (!getAddress().isListeningSocket()) {
throw new InvalidBusAddressException("Cannot listen on client connection address (try use connect() instead)");
}
transportConnection = internalConnect();
return transportConnection;
}
private TransportConnection internalConnect() throws IOException {
if (config.getPreConnectCallback() != null) {
config.getPreConnectCallback().accept(this);
}
SocketChannel channel = connectImpl();
authenticate(channel);
return createInputOutput(channel);
}
/**
* Set a callback which will be called right before the connection will
* be established to the transport.
*
* @param _run runnable to execute, null if no callback should be executed
*
* @since 4.2.0 - 2022-07-20
*/
public void setPreConnectCallback(Consumer<AbstractTransport> _run) {
config.setPreConnectCallback(_run);
}
/**
* Helper method to authenticate to DBus using SASL.
*
* @param _sock socketchannel
* @throws IOException on any error
*/
private void authenticate(SocketChannel _sock) throws IOException {
SASL sasl = new SASL(config.getSaslConfig());
try {
if (!sasl.auth(_sock, this)) {
throw new AuthenticationException("Failed to authenticate");
}
} catch (IOException _ex) {
_sock.close();
throw _ex;
}
fileDescriptorSupported = sasl.isFileDescriptorSupported(); // false if server does not support file descriptors
}
/**
* Setup message reader/writer.
* Will look for SPI provider first, if none is found default implementation is used.
* The default implementation does not support file descriptor passing!
*
* @param _socket socket to use
* @return TransportConnection with configured socket channel, reader and writer
*/
private TransportConnection createInputOutput(SocketChannel _socket) {
IMessageReader reader = null;
IMessageWriter writer = null;
try {
for (ISocketProvider provider : spiLoader) {
logger.debug("Found ISocketProvider {}", provider);
provider.setFileDescriptorSupport(hasFileDescriptorSupport() && fileDescriptorSupported);
reader = provider.createReader(_socket);
writer = provider.createWriter(_socket);
if (reader != null && writer != null) {
logger.debug("Using ISocketProvider {}", provider);
break;
}
}
} catch (ServiceConfigurationError _ex) {
logger.error("Could not initialize service provider", _ex);
} catch (IOException _ex) {
logger.error("Could not initialize alternative message reader/writer", _ex);
}
if (reader == null || writer == null) {
logger.debug("No alternative ISocketProvider found, using built-in implementation");
reader = new InputStreamMessageReader(_socket);
writer = new OutputStreamMessageWriter(_socket);
fileDescriptorSupported = false; // internal implementation does not support file descriptors even if server allows it
}
return new TransportConnection(_socket, writer, reader);
}
/**
* Returns the {@link BusAddress} used for this transport.
*
* @return BusAddress, never null
*/
protected BusAddress getAddress() {
return address;
}
/**
* Get the logger in subclasses.
*
* @return Logger, never null
*/
protected Logger getLogger() {
return logger;
}
/**
* Returns the current configuration used for SASL authentication.
*
* @return SaslConfig, never null
*/
protected SaslConfig getSaslConfig() {
return config.getSaslConfig();
}
/**
* Set the SASL authentication mode.
*
* @deprecated please use {@link #getSaslConfig()}.getAuthMode() instead
*/
@Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true)
protected int getSaslAuthMode() {
return getSaslConfig().getAuthMode();
}
/**
* Set the SASL authentication mode.
*
* @deprecated please use {@link #getSaslConfig()}.getMode() instead
*/
@Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true)
protected SASL.SaslMode getSaslMode() {
return getSaslConfig().getMode();
}
/**
* Set the SASL mode (server or client).
*
* @param _saslMode mode to set
* @deprecated please use {@link #getSaslConfig()}.setMode(int) instead
*/
@Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true)
protected void setSaslMode(SASL.SaslMode _saslMode) {
getSaslConfig().setMode(_saslMode);
}
/**
* Set the SASL authentication mode.
*
* @param _mode mode to set
* @deprecated please use {@link #getSaslConfig()}.setSaslAuthMode(int) instead
*/
@Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true)
protected void setSaslAuthMode(int _mode) {
getSaslConfig().setAuthMode(_mode);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(" [id=")
.append(transportId)
.append(", ");
if (transportConnection != null) {
sb.append("connectionId=")
.append(transportConnection.getId())
.append(", ");
}
sb.append("address=")
.append(address)
.append("]");
return sb.toString();
}
@Override
public void close() throws IOException {
if (transportConnection != null) {
transportConnection.close();
transportConnection = null;
}
}
}

View file

@ -0,0 +1,17 @@
package org.freedesktop.dbus.connections.transports;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.config.TransportConfig;
import java.io.IOException;
import java.nio.channels.SocketChannel;
public abstract class AbstractUnixTransport extends AbstractTransport {
protected AbstractUnixTransport(BusAddress _address, TransportConfig _config) {
super(_address, _config);
}
public abstract int getUid(SocketChannel _sock) throws IOException;
}

View file

@ -0,0 +1,18 @@
package org.freedesktop.dbus.connections.transports;
import org.freedesktop.dbus.connections.BusAddress;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
/**
* Interface which should be implemented by {@link BusAddress} subclasses which use
* files as 'address' (e.g. unix sockets) and needs to set permission on those files.
*
* @author hypfvieh
* @since 4.2.0 - 2022-07-18
*/
public interface IFileBasedBusAddress {
void updatePermissions(String _fileOwner, String _fileGroup, Set<PosixFilePermission> _fileUnixPermissions);
}

Some files were not shown because too many files have changed in this diff Show more