Upgrade dbus-java to 4.3.0. Incorporated: dbus-java-core and dbus-java-transport-native-unixsocket
This commit is contained in:
parent
5aef59acd8
commit
87905c186d
193 changed files with 17165 additions and 9821 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 => 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>
|
||||
* <class> = <debuglevel>
|
||||
* </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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,103 +1,115 @@
|
|||
/*
|
||||
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.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.freedesktop.dbus.Gettext.getString;
|
||||
|
||||
class ArrayFrob {
|
||||
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>>();
|
||||
|
||||
public final class ArrayFrob {
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE = new ConcurrentHashMap<>();
|
||||
static {
|
||||
primitiveToWrapper.put(Boolean.TYPE, Boolean.class);
|
||||
primitiveToWrapper.put(Byte.TYPE, Byte.class);
|
||||
primitiveToWrapper.put(Short.TYPE, Short.class);
|
||||
primitiveToWrapper.put(Character.TYPE, Character.class);
|
||||
primitiveToWrapper.put(Integer.TYPE, Integer.class);
|
||||
primitiveToWrapper.put(Long.TYPE, Long.class);
|
||||
primitiveToWrapper.put(Float.TYPE, Float.class);
|
||||
primitiveToWrapper.put(Double.TYPE, Double.class);
|
||||
wrapperToPrimitive.put(Boolean.class, Boolean.TYPE);
|
||||
wrapperToPrimitive.put(Byte.class, Byte.TYPE);
|
||||
wrapperToPrimitive.put(Short.class, Short.TYPE);
|
||||
wrapperToPrimitive.put(Character.class, Character.TYPE);
|
||||
wrapperToPrimitive.put(Integer.class, Integer.TYPE);
|
||||
wrapperToPrimitive.put(Long.class, Long.TYPE);
|
||||
wrapperToPrimitive.put(Float.class, Float.TYPE);
|
||||
wrapperToPrimitive.put(Double.class, Double.TYPE);
|
||||
PRIMITIVE_TO_WRAPPER.put(Boolean.TYPE, Boolean.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Byte.TYPE, Byte.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Short.TYPE, Short.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Character.TYPE, Character.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Integer.TYPE, Integer.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Long.TYPE, Long.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Float.TYPE, Float.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(Double.TYPE, Double.class);
|
||||
WRAPPER_TO_PRIMITIVE.put(Boolean.class, Boolean.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Byte.class, Byte.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Short.class, Short.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Character.class, Character.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Integer.class, Integer.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Long.class, Long.TYPE);
|
||||
WRAPPER_TO_PRIMITIVE.put(Float.class, Float.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")
|
||||
public static <T> T[] wrap(Object o) throws IllegalArgumentException {
|
||||
Class<? extends Object> ac = o.getClass();
|
||||
if (!ac.isArray()) throw new IllegalArgumentException(getString("invalidArray"));
|
||||
public static <T> T[] wrap(Object _o) throws IllegalArgumentException {
|
||||
Class<? extends Object> ac = _o.getClass();
|
||||
if (!ac.isArray()) {
|
||||
throw new IllegalArgumentException("Not an array");
|
||||
}
|
||||
Class<? extends Object> cc = ac.getComponentType();
|
||||
Class<? extends Object> ncc = primitiveToWrapper.get(cc);
|
||||
if (null == ncc) throw new IllegalArgumentException(getString("notPrimitiveType"));
|
||||
T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o));
|
||||
for (int i = 0; i < ns.length; i++)
|
||||
ns[i] = (T) Array.get(o, i);
|
||||
Class<? extends Object> ncc = PRIMITIVE_TO_WRAPPER.get(cc);
|
||||
if (null == ncc) {
|
||||
throw new IllegalArgumentException("Not a primitive type");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Object unwrap(T[] ns) throws IllegalArgumentException {
|
||||
Class<? extends T[]> ac = (Class<? extends T[]>) ns.getClass();
|
||||
public static <T> Object unwrap(T[] _ns) throws IllegalArgumentException {
|
||||
Class<? extends T[]> ac = (Class<? extends T[]>) _ns.getClass();
|
||||
Class<T> cc = (Class<T>) ac.getComponentType();
|
||||
Class<? extends Object> ncc = wrapperToPrimitive.get(cc);
|
||||
if (null == ncc) throw new IllegalArgumentException(getString("invalidWrapperType"));
|
||||
Object o = Array.newInstance(ncc, ns.length);
|
||||
for (int i = 0; i < ns.length; i++)
|
||||
Array.set(o, i, ns[i]);
|
||||
Class<? extends Object> ncc = WRAPPER_TO_PRIMITIVE.get(cc);
|
||||
if (null == ncc) {
|
||||
throw new IllegalArgumentException("Not a wrapper type");
|
||||
}
|
||||
Object o = Array.newInstance(ncc, _ns.length);
|
||||
for (int i = 0; i < _ns.length; i++) {
|
||||
Array.set(o, i, _ns[i]);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
public static <T> List<T> listify(T[] ns) throws IllegalArgumentException {
|
||||
return Arrays.asList(ns);
|
||||
public static <T> List<T> listify(T[] _ns) throws IllegalArgumentException {
|
||||
return Arrays.asList(_ns);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> listify(Object o) throws IllegalArgumentException {
|
||||
if (o instanceof Object[]) return listify((T[]) o);
|
||||
if (!o.getClass().isArray()) throw new IllegalArgumentException(getString("invalidArray"));
|
||||
List<T> l = new ArrayList<T>(Array.getLength(o));
|
||||
for (int i = 0; i < Array.getLength(o); i++)
|
||||
l.add((T) Array.get(o, i));
|
||||
public static <T> List<T> listify(Object _o) throws IllegalArgumentException {
|
||||
if (_o instanceof Object[]) {
|
||||
return listify((T[]) _o);
|
||||
}
|
||||
if (!_o.getClass().isArray()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] delist(List<T> l, Class<T> c) throws IllegalArgumentException {
|
||||
return l.toArray((T[]) Array.newInstance(c, 0));
|
||||
public static <T> T[] delist(List<T> _l, Class<T> _c) throws IllegalArgumentException {
|
||||
return _l.toArray((T[]) Array.newInstance(_c, 0));
|
||||
}
|
||||
|
||||
public static <T> Object delistprimitive(List<T> l, Class<T> c) throws IllegalArgumentException {
|
||||
Object o = Array.newInstance(c, l.size());
|
||||
for (int i = 0; i < l.size(); i++)
|
||||
Array.set(o, i, l.get(i));
|
||||
public static <T> Object delistprimitive(List<T> _l, Class<T> _c) throws IllegalArgumentException {
|
||||
Object o = Array.newInstance(_c, _l.size());
|
||||
for (int i = 0; i < _l.size(); i++) {
|
||||
Array.set(o, i, _l.get(i));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
@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:
|
||||
*
|
||||
** List<Integer> -> List<Integer>
|
||||
|
@ -112,62 +124,57 @@ class ArrayFrob {
|
|||
*/
|
||||
try {
|
||||
// List<Integer> -> List<Integer>
|
||||
if (List.class.equals(c)
|
||||
&& o instanceof List)
|
||||
return o;
|
||||
if (List.class.equals(_c) && _o instanceof List) {
|
||||
return _o;
|
||||
}
|
||||
|
||||
// int[] -> List<Integer>
|
||||
// Integer[] -> List<Integer>
|
||||
if (List.class.equals(c)
|
||||
&& o.getClass().isArray())
|
||||
return listify(o);
|
||||
if (List.class.equals(_c) && _o.getClass().isArray()) {
|
||||
return listify(_o);
|
||||
}
|
||||
|
||||
// int[] -> int[]
|
||||
// Integer[] -> Integer[]
|
||||
if (o.getClass().isArray()
|
||||
&& c.isArray()
|
||||
&& o.getClass().getComponentType().equals(c.getComponentType()))
|
||||
return o;
|
||||
if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().equals(_c.getComponentType())) {
|
||||
return _o;
|
||||
}
|
||||
|
||||
// int[] -> Integer[]
|
||||
if (o.getClass().isArray()
|
||||
&& c.isArray()
|
||||
&& o.getClass().getComponentType().isPrimitive())
|
||||
return wrap(o);
|
||||
if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().isPrimitive()) {
|
||||
return wrap(_o);
|
||||
}
|
||||
|
||||
// Integer[] -> int[]
|
||||
if (o.getClass().isArray()
|
||||
&& c.isArray()
|
||||
&& c.getComponentType().isPrimitive())
|
||||
return unwrap((Object[]) o);
|
||||
if (_o.getClass().isArray() && _c.isArray() && _c.getComponentType().isPrimitive()) {
|
||||
return unwrap((Object[]) _o);
|
||||
}
|
||||
|
||||
// List<Integer> -> int[]
|
||||
if (o instanceof List
|
||||
&& c.isArray()
|
||||
&& c.getComponentType().isPrimitive())
|
||||
return delistprimitive((List<Object>) o, (Class<Object>) c.getComponentType());
|
||||
if (_o instanceof List && _c.isArray() && _c.getComponentType().isPrimitive()) {
|
||||
return delistprimitive((List<Object>) _o, (Class<Object>) _c.getComponentType());
|
||||
}
|
||||
|
||||
// List<Integer> -> Integer[]
|
||||
if (o instanceof List
|
||||
&& c.isArray())
|
||||
return delist((List<Object>) o, (Class<Object>) c.getComponentType());
|
||||
|
||||
if (o.getClass().isArray()
|
||||
&& c.isArray())
|
||||
return type((Object[]) o, (Class<Object>) c.getComponentType());
|
||||
|
||||
} catch (Exception e) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
|
||||
throw new IllegalArgumentException(e);
|
||||
if (_o instanceof List && _c.isArray()) {
|
||||
return delist((List<Object>) _o, (Class<Object>) _c.getComponentType());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(MessageFormat.format(getString("convertionTypeNotExpected"), new Object[]{o.getClass(), c}));
|
||||
if (_o.getClass().isArray() && _c.isArray()) {
|
||||
return type((Object[]) _o, (Class<Object>) _c.getComponentType());
|
||||
}
|
||||
|
||||
public static Object[] type(Object[] old, Class<Object> c) {
|
||||
Object[] ns = (Object[]) Array.newInstance(c, old.length);
|
||||
for (int i = 0; i < ns.length; i++)
|
||||
ns[i] = old[i];
|
||||
} catch (Exception _ex) {
|
||||
LoggerFactory.getLogger(ArrayFrob.class).debug("Cannot convert object.", _ex);
|
||||
throw new IllegalArgumentException(_ex);
|
||||
}
|
||||
|
||||
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) {
|
||||
Object[] ns = (Object[]) Array.newInstance(_c, _old.length);
|
||||
System.arraycopy(_old, 0, ns, 0, ns.length);
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
import org.freedesktop.dbus.annotations.Position;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This class is the super class of both Structs and Tuples
|
||||
* and holds common methods.
|
||||
*/
|
||||
abstract class Container {
|
||||
private static Map<Type, Type[]> typecache = new HashMap<Type, Type[]>();
|
||||
|
||||
static void putTypeCache(Type k, Type[] v) {
|
||||
typecache.put(k, v);
|
||||
}
|
||||
|
||||
static Type[] getTypeCache(Type k) {
|
||||
return typecache.get(k);
|
||||
}
|
||||
|
||||
public abstract class Container {
|
||||
private static final Map<Type, Type[]> TYPE_CACHE = new HashMap<>();
|
||||
private Object[] parameters = null;
|
||||
|
||||
public Container() {
|
||||
Container() {
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
|
@ -43,13 +29,16 @@ abstract class Container {
|
|||
int diff = 0;
|
||||
for (Field f : fs) {
|
||||
Position p = f.getAnnotation(Position.class);
|
||||
f.setAccessible(true);
|
||||
|
||||
if (null == p) {
|
||||
diff++;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
args[p.value()] = f.get(this);
|
||||
} catch (IllegalAccessException IAe) {
|
||||
} catch (IllegalAccessException _exIa) {
|
||||
LoggerFactory.getLogger(getClass()).trace("Could not set value", _exIa);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,35 +48,66 @@ abstract class Container {
|
|||
|
||||
/**
|
||||
* Returns the struct contents in order.
|
||||
*
|
||||
* @throws DBusException If there is a problem doing this.
|
||||
* @return object array
|
||||
*/
|
||||
public final Object[] getParameters() {
|
||||
if (null != parameters) return parameters;
|
||||
if (null != parameters) {
|
||||
return parameters;
|
||||
}
|
||||
setup();
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this struct as a string.
|
||||
*/
|
||||
/** Returns this struct as a string. */
|
||||
@Override
|
||||
public final String toString() {
|
||||
String s = getClass().getName() + "<";
|
||||
if (null == parameters)
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getName()).append("<");
|
||||
if (null == parameters) {
|
||||
setup();
|
||||
if (0 == parameters.length)
|
||||
return s + ">";
|
||||
for (Object o : parameters)
|
||||
s += o + ", ";
|
||||
return s.replaceAll(", $", ">");
|
||||
}
|
||||
if (0 == parameters.length) {
|
||||
return sb.append(">").toString();
|
||||
}
|
||||
sb.append(Arrays.stream(parameters).map(o -> Objects.toString(o)).collect(Collectors.joining(", ")));
|
||||
return sb.append(">").toString();
|
||||
}
|
||||
|
||||
public final boolean equals(Object other) {
|
||||
if (other instanceof Container) {
|
||||
Container that = (Container) other;
|
||||
if (this.getClass().equals(that.getClass()))
|
||||
return Arrays.equals(this.getParameters(), that.getParameters());
|
||||
else return false;
|
||||
} else return false;
|
||||
@Override
|
||||
public final boolean equals(Object _other) {
|
||||
if (this == _other) {
|
||||
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());
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,71 +1,53 @@
|
|||
/*
|
||||
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.Error.NoReply;
|
||||
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.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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import static org.freedesktop.dbus.Gettext.getString;
|
||||
|
||||
/**
|
||||
* A handle to an asynchronous method call.
|
||||
*/
|
||||
public class DBusAsyncReply<ReturnType> {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
public class DBusAsyncReply<T> {
|
||||
|
||||
private ReturnType rval = null;
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private T rval = null;
|
||||
private DBusExecutionException error = null;
|
||||
private MethodCall mc;
|
||||
private Method me;
|
||||
private AbstractConnection conn;
|
||||
private final MethodCall mc;
|
||||
private final Method me;
|
||||
private final AbstractConnection conn;
|
||||
|
||||
DBusAsyncReply(MethodCall mc, Method me, AbstractConnection conn) {
|
||||
this.mc = mc;
|
||||
this.me = me;
|
||||
this.conn = conn;
|
||||
public DBusAsyncReply(MethodCall _mc, Method _me, AbstractConnection _conn) {
|
||||
this.mc = _mc;
|
||||
this.me = _me;
|
||||
this.conn = _conn;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void checkReply() {
|
||||
if (mc.hasReply()) {
|
||||
Message m = mc.getReply();
|
||||
if (m instanceof Error)
|
||||
if (m instanceof Error) {
|
||||
error = ((Error) m).getException();
|
||||
else if (m instanceof MethodReturn) {
|
||||
} else if (m instanceof MethodReturn) {
|
||||
try {
|
||||
rval = (ReturnType) RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn);
|
||||
} catch (DBusExecutionException DBEe) {
|
||||
error = DBEe;
|
||||
} catch (DBusException DBe) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
|
||||
error = new DBusExecutionException(DBe.getMessage());
|
||||
Object obj = RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn);
|
||||
|
||||
rval = (T) obj;
|
||||
} catch (DBusExecutionException _ex) {
|
||||
error = _ex;
|
||||
} catch (DBusException _ex) {
|
||||
logger.debug("", _ex);
|
||||
error = new DBusExecutionException(_ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,45 +55,68 @@ public class DBusAsyncReply<ReturnType> {
|
|||
|
||||
/**
|
||||
* Check if we've had a reply.
|
||||
*
|
||||
* @return True if we have a reply
|
||||
* @return true if we have a reply
|
||||
*/
|
||||
public boolean hasReply() {
|
||||
if (null != rval || null != error) return true;
|
||||
if (null != rval || null != error) {
|
||||
return true;
|
||||
}
|
||||
checkReply();
|
||||
return null != rval || null != error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reply.
|
||||
*
|
||||
* @return The return value from the method.
|
||||
* @throws DBusExecutionException if the reply to the method was an error.
|
||||
* @throws DBusException if the reply to the method was an error.
|
||||
* @throws NoReply if the method hasn't had a reply yet
|
||||
*/
|
||||
public ReturnType getReply() throws DBusExecutionException {
|
||||
if (null != rval) return rval;
|
||||
else if (null != error) throw error;
|
||||
public T getReply() throws DBusException {
|
||||
if (null != rval) {
|
||||
return rval;
|
||||
} else if (null != error) {
|
||||
throw error;
|
||||
}
|
||||
checkReply();
|
||||
if (null != rval) return rval;
|
||||
else if (null != error) throw error;
|
||||
else throw new NoReply(getString("asyncCallNoReply"));
|
||||
if (null != rval) {
|
||||
return rval;
|
||||
} else if (null != error) {
|
||||
throw error;
|
||||
} else {
|
||||
throw new NoReply("Async call has not had a reply");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getString("waitingFor") + mc;
|
||||
return "Waiting for: " + mc;
|
||||
}
|
||||
|
||||
Method getMethod() {
|
||||
public Method getMethod() {
|
||||
return me;
|
||||
}
|
||||
|
||||
AbstractConnection getConnection() {
|
||||
public AbstractConnection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
|
||||
MethodCall getCall() {
|
||||
public MethodCall getCall() {
|
||||
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;
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
/*
|
||||
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.messages.Message;
|
||||
|
||||
/**
|
||||
* Holds information on a method call
|
||||
*/
|
||||
|
@ -19,59 +11,59 @@ public class DBusCallInfo {
|
|||
*/
|
||||
public static final int NO_REPLY = Message.Flags.NO_REPLY_EXPECTED;
|
||||
public static final int ASYNC = 0x100;
|
||||
private String source;
|
||||
private String destination;
|
||||
private String objectpath;
|
||||
private String iface;
|
||||
private String method;
|
||||
private int flags;
|
||||
private final String source;
|
||||
private final String destination;
|
||||
private final String objectpath;
|
||||
private final String iface;
|
||||
private final String method;
|
||||
private final int flags;
|
||||
|
||||
DBusCallInfo(Message m) {
|
||||
this.source = m.getSource();
|
||||
this.destination = m.getDestination();
|
||||
this.objectpath = m.getPath();
|
||||
this.iface = m.getInterface();
|
||||
this.method = m.getName();
|
||||
this.flags = m.getFlags();
|
||||
public DBusCallInfo(Message _m) {
|
||||
source = _m.getSource();
|
||||
destination = _m.getDestination();
|
||||
objectpath = _m.getPath();
|
||||
iface = _m.getInterface();
|
||||
method = _m.getName();
|
||||
flags = _m.getFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BusID which called the method
|
||||
/** Returns the BusID which called the method.
|
||||
* @return source
|
||||
*/
|
||||
public String getSource() {
|
||||
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() {
|
||||
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() {
|
||||
return objectpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interface this method was called with
|
||||
/** Returns the interface this method was called with.
|
||||
* @return interface
|
||||
*/
|
||||
public String getInterface() {
|
||||
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() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any flags set on this method call
|
||||
/** Returns any flags set on this method call.
|
||||
* @return flags
|
||||
*/
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class DBusMap<K, V> implements Map<K, V> {
|
||||
public class DBusMap<K, V> implements Map<K, V> {
|
||||
// CHECKSTYLE:OFF
|
||||
Object[][] entries;
|
||||
// CHECKSTYLE:ON
|
||||
public DBusMap(Object[][] _entries) {
|
||||
this.entries = _entries;
|
||||
}
|
||||
|
||||
public DBusMap(Object[][] entries) {
|
||||
this.entries = entries;
|
||||
@Override
|
||||
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> {
|
||||
private int entry;
|
||||
private final int entry;
|
||||
|
||||
public Entry(int i) {
|
||||
this.entry = i;
|
||||
Entry(int _i) {
|
||||
this.entry = _i;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (null == o) return false;
|
||||
if (!(o instanceof DBusMap.Entry)) return false;
|
||||
return this.entry == ((Entry) o).entry;
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object _o) {
|
||||
if (null == _o) {
|
||||
return false;
|
||||
}
|
||||
if (!(_o instanceof DBusMap.Entry)) {
|
||||
return false;
|
||||
}
|
||||
return this.entry == ((Entry) _o).entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public K getKey() {
|
||||
return (K) entries[entry][0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V getValue() {
|
||||
return (V) entries[entry][1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entries[entry][0].hashCode();
|
||||
}
|
||||
|
||||
public V setValue(V value) {
|
||||
@Override
|
||||
public V setValue(V _value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int compareTo(Entry e) {
|
||||
return entry - e.entry;
|
||||
@Override
|
||||
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(".$", " }");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
import org.freedesktop.dbus.errors.Error;
|
||||
import org.freedesktop.dbus.exceptions.DBusException;
|
||||
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 static org.freedesktop.dbus.Gettext.getString;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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 {
|
||||
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 */
|
||||
private String type;
|
||||
private String iface;
|
||||
private String member;
|
||||
private String object;
|
||||
private String source;
|
||||
private static HashMap<String, Class<? extends DBusSignal>> signalTypeMap =
|
||||
new HashMap<String, Class<? extends DBusSignal>>();
|
||||
private final String type;
|
||||
private final String iface;
|
||||
private final String member;
|
||||
private final String object;
|
||||
private final String source;
|
||||
|
||||
static Class<? extends DBusSignal> getCachedSignalType(String type) {
|
||||
return signalTypeMap.get(type);
|
||||
public DBusMatchRule(String _type, String _iface, String _member) {
|
||||
this(_type, _iface, _member, null);
|
||||
}
|
||||
|
||||
public DBusMatchRule(String type, String iface, String member) {
|
||||
this.type = type;
|
||||
this.iface = iface;
|
||||
this.member = member;
|
||||
public DBusMatchRule(String _type, String _iface, String _member, String _object) {
|
||||
type = _type;
|
||||
iface = _iface;
|
||||
member = _member;
|
||||
object = _object;
|
||||
source = null;
|
||||
}
|
||||
|
||||
public DBusMatchRule(DBusExecutionException e) throws DBusException {
|
||||
this(e.getClass());
|
||||
member = null;
|
||||
type = "error";
|
||||
public DBusMatchRule(DBusExecutionException _e) throws DBusException {
|
||||
this(_e.getClass());
|
||||
}
|
||||
|
||||
public DBusMatchRule(Message m) {
|
||||
iface = m.getInterface();
|
||||
member = m.getName();
|
||||
if (m instanceof DBusSignal)
|
||||
public DBusMatchRule(Message _m) {
|
||||
iface = _m.getInterface();
|
||||
source = null;
|
||||
object = null;
|
||||
member = _m instanceof Error ? null : _m.getName();
|
||||
if (_m instanceof DBusSignal) {
|
||||
type = "signal";
|
||||
else if (m instanceof Error) {
|
||||
} else if (_m instanceof Error) {
|
||||
type = "error";
|
||||
member = null;
|
||||
} else if (m instanceof MethodCall)
|
||||
} else if (_m instanceof MethodCall) {
|
||||
type = "method_call";
|
||||
else if (m instanceof MethodReturn)
|
||||
} else if (_m instanceof MethodReturn) {
|
||||
type = "method_reply";
|
||||
} else {
|
||||
type = null;
|
||||
}
|
||||
}
|
||||
|
||||
public DBusMatchRule(Class<? extends DBusInterface> c, String method) throws DBusException {
|
||||
this(c);
|
||||
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;
|
||||
public DBusMatchRule(Class<? extends DBusInterface> _c, String _method) throws DBusException {
|
||||
this(_c, null, null, "method_call", _method);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBusMatchRule(Class<? extends Object> c) throws DBusException {
|
||||
if (DBusInterface.class.isAssignableFrom(c)) {
|
||||
if (null != c.getAnnotation(DBusInterfaceName.class))
|
||||
iface = c.getAnnotation(DBusInterfaceName.class).value();
|
||||
else
|
||||
iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".");
|
||||
if (!iface.matches(".*\\..*"))
|
||||
throw new DBusException(getString("interfaceMustBeDefinedPackage"));
|
||||
member = null;
|
||||
type = null;
|
||||
} else if (DBusSignal.class.isAssignableFrom(c)) {
|
||||
if (null == 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(".");
|
||||
DBusMatchRule(Class<? extends Object> _c, String _source, String _object, String _type, String _member) throws DBusException {
|
||||
if (DBusInterface.class.isAssignableFrom(_c)) {
|
||||
iface = DBusNamingUtil.getInterfaceName(_c);
|
||||
assertDBusInterface(iface);
|
||||
|
||||
member = _member != null ? _member : null;
|
||||
type = _type != null ? _type : null;
|
||||
} else if (DBusSignal.class.isAssignableFrom(_c)) {
|
||||
if (null == _c.getEnclosingClass()) {
|
||||
throw new DBusException("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package.");
|
||||
}
|
||||
iface = DBusNamingUtil.getInterfaceName(_c.getEnclosingClass());
|
||||
// Don't export things which are invalid D-Bus interfaces
|
||||
if (!iface.matches(".*\\..*"))
|
||||
throw new DBusException(getString("interfaceMustBeDefinedPackage"));
|
||||
if (c.isAnnotationPresent(DBusMemberName.class))
|
||||
member = c.getAnnotation(DBusMemberName.class).value();
|
||||
else
|
||||
member = c.getSimpleName();
|
||||
signalTypeMap.put(iface + '$' + member, (Class<? extends DBusSignal>) c);
|
||||
type = "signal";
|
||||
} else if (Error.class.isAssignableFrom(c)) {
|
||||
if (null != c.getAnnotation(DBusInterfaceName.class))
|
||||
iface = c.getAnnotation(DBusInterfaceName.class).value();
|
||||
else
|
||||
iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".");
|
||||
if (!iface.matches(".*\\..*"))
|
||||
throw new DBusException(getString("interfaceMustBeDefinedPackage"));
|
||||
member = null;
|
||||
type = "error";
|
||||
} else if (DBusExecutionException.class.isAssignableFrom(c)) {
|
||||
if (null != c.getClass().getAnnotation(DBusInterfaceName.class))
|
||||
iface = c.getClass().getAnnotation(DBusInterfaceName.class).value();
|
||||
else
|
||||
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);
|
||||
assertDBusInterface(iface);
|
||||
|
||||
member = _member != null ? _member : DBusNamingUtil.getSignalName(_c);
|
||||
SIGNALTYPEMAP.put(iface + '$' + member, (Class<? extends DBusSignal>) _c);
|
||||
type = _type != null ? _type : "signal";
|
||||
} else if (Error.class.isAssignableFrom(_c)) {
|
||||
iface = DBusNamingUtil.getInterfaceName(_c);
|
||||
assertDBusInterface(iface);
|
||||
member = _member != null ? _member : null;
|
||||
type = _type != null ? _type : "error";
|
||||
} else if (DBusExecutionException.class.isAssignableFrom(_c)) {
|
||||
iface = DBusNamingUtil.getInterfaceName(_c);
|
||||
assertDBusInterface(iface);
|
||||
member = _member != null ? _member : null;
|
||||
type = _type != null ? _type : "error";
|
||||
} else {
|
||||
throw new DBusException("Invalid type for match rule: " + _c);
|
||||
}
|
||||
|
||||
source = _source;
|
||||
object = _object;
|
||||
}
|
||||
|
||||
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() {
|
||||
String s = null;
|
||||
if (null != type) s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'";
|
||||
if (null != member) 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 + "'";
|
||||
if (null != type) {
|
||||
s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'";
|
||||
}
|
||||
if (null != member) {
|
||||
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;
|
||||
}
|
||||
|
||||
@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() {
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 => 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
File diff suppressed because it is too large
Load diff
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
import cx.ath.matthew.debug.Debug;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class MethodTuple {
|
||||
String name;
|
||||
String sig;
|
||||
import java.util.Objects;
|
||||
|
||||
public MethodTuple(String name, String sig) {
|
||||
this.name = name;
|
||||
if (null != sig)
|
||||
this.sig = sig;
|
||||
else
|
||||
this.sig = "";
|
||||
if (Debug.debug) Debug.print(Debug.VERBOSE, "new MethodTuple(" + this.name + ", " + this.sig + ")");
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o.getClass().equals(MethodTuple.class)
|
||||
&& ((MethodTuple) o).name.equals(this.name)
|
||||
&& ((MethodTuple) o).sig.equals(this.sig);
|
||||
public class MethodTuple {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final String name;
|
||||
private final String sig;
|
||||
|
||||
public MethodTuple(String _name, String _sig) {
|
||||
name = _name;
|
||||
if (null != _sig) {
|
||||
sig = _sig;
|
||||
} else {
|
||||
sig = "";
|
||||
}
|
||||
logger.trace("new MethodTuple({}, {})", name, sig);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
class ObjectPath extends Path {
|
||||
public String source;
|
||||
public class ObjectPath extends DBusPath {
|
||||
private String source;
|
||||
|
||||
// public DBusConnection conn;
|
||||
public ObjectPath(String source, String path/*, DBusConnection conn*/) {
|
||||
super(path);
|
||||
this.source = source;
|
||||
// this.conn = conn;
|
||||
public ObjectPath(String _source, String _path) {
|
||||
super(_path);
|
||||
this.source = _source;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String _source) {
|
||||
source = _source;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
|
||||
import cx.ath.matthew.debug.Debug;
|
||||
import org.freedesktop.DBus;
|
||||
import org.freedesktop.dbus.annotations.MethodNoReply;
|
||||
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.DBusExecutionException;
|
||||
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.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.freedesktop.dbus.Gettext.getString;
|
||||
|
||||
class RemoteInvocationHandler implements InvocationHandler {
|
||||
public class RemoteInvocationHandler implements InvocationHandler {
|
||||
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 Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException {
|
||||
Class<? extends Object> c = m.getReturnType();
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteInvocationHandler.class);
|
||||
// CHECKSTYLE:OFF
|
||||
AbstractConnection conn;
|
||||
RemoteObject remote;
|
||||
// CHECKSTYLE:ON
|
||||
|
||||
if (null == rp) {
|
||||
if (null == c || Void.TYPE.equals(c)) return null;
|
||||
else throw new DBusExecutionException(getString("voidReturnType"));
|
||||
public RemoteInvocationHandler(AbstractConnection _conn, RemoteObject _remote) {
|
||||
this.remote = _remote;
|
||||
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 {
|
||||
try {
|
||||
if (Debug.debug)
|
||||
Debug.print(Debug.VERBOSE, "Converting return parameters from " + Arrays.deepToString(rp) + " to type " + m.getGenericReturnType());
|
||||
rp = Marshalling.deSerializeParameters(rp,
|
||||
new Type[]{m.getGenericReturnType()}, conn);
|
||||
} catch (Exception e) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
|
||||
throw new DBusExecutionException(MessageFormat.format(getString("invalidReturnType"), new Object[]{e.getMessage()}));
|
||||
LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> LOGGER.trace("Converting return parameters from {} to type {}",
|
||||
Arrays.deepToString(_rp), _m.getGenericReturnType()));
|
||||
|
||||
rp = Marshalling.deSerializeParameters(rp, new Type[] {
|
||||
_m.getGenericReturnType()
|
||||
}, _conn);
|
||||
} 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) {
|
||||
case 0:
|
||||
if (null == c || Void.TYPE.equals(c))
|
||||
if (null == c || Void.TYPE.equals(c)) {
|
||||
return null;
|
||||
else throw new DBusExecutionException(getString("voidReturnType"));
|
||||
} else {
|
||||
throw new DBusException("Wrong return type (got void, expected a value)");
|
||||
}
|
||||
case 1:
|
||||
return rp[0];
|
||||
default:
|
||||
|
||||
// check we are meant to return multiple values
|
||||
if (!Tuple.class.isAssignableFrom(c))
|
||||
throw new DBusExecutionException(getString("tupleReturnType"));
|
||||
if (!Tuple.class.isAssignableFrom(c)) {
|
||||
throw new DBusException("Wrong return type (not expecting Tuple)");
|
||||
}
|
||||
|
||||
Constructor<? extends Object> cons = c.getConstructors()[0];
|
||||
try {
|
||||
return cons.newInstance(rp);
|
||||
} catch (Exception e) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
|
||||
throw new DBusException(e.getMessage());
|
||||
} catch (Exception _ex) {
|
||||
LOGGER.debug("", _ex);
|
||||
throw new DBusException(_ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, int syncmethod, CallbackHandler callback, Object... args) throws DBusExecutionException {
|
||||
Type[] ts = m.getGenericParameterTypes();
|
||||
public static Object executeRemoteMethod(RemoteObject _ro, Method _m, AbstractConnection _conn, int _syncmethod, CallbackHandler<?> _callback, Object... _args) throws DBusException {
|
||||
Type[] ts = _m.getGenericParameterTypes();
|
||||
String sig = null;
|
||||
if (ts.length > 0) try {
|
||||
Object[] args = _args;
|
||||
if (ts.length > 0) {
|
||||
try {
|
||||
sig = Marshalling.getDBusType(ts);
|
||||
args = Marshalling.convertParameters(args, ts, conn);
|
||||
} catch (DBusException DBe) {
|
||||
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;
|
||||
byte flags = 0;
|
||||
if (!ro.autostart) flags |= Message.Flags.NO_AUTO_START;
|
||||
if (syncmethod == CALL_TYPE_ASYNC) flags |= Message.Flags.ASYNC;
|
||||
if (m.isAnnotationPresent(DBus.Method.NoReply.class)) flags |= Message.Flags.NO_REPLY_EXPECTED;
|
||||
if (!_ro.isAutostart()) {
|
||||
flags |= Message.Flags.NO_AUTO_START;
|
||||
}
|
||||
if (_syncmethod == CALL_TYPE_ASYNC) {
|
||||
flags |= Message.Flags.ASYNC;
|
||||
}
|
||||
if (_m.isAnnotationPresent(MethodNoReply.class)) {
|
||||
flags |= Message.Flags.NO_REPLY_EXPECTED;
|
||||
}
|
||||
try {
|
||||
String name;
|
||||
if (m.isAnnotationPresent(DBusMemberName.class))
|
||||
name = m.getAnnotation(DBusMemberName.class).value();
|
||||
else
|
||||
name = m.getName();
|
||||
if (null == ro.iface)
|
||||
call = new MethodCall(ro.busname, ro.objectpath, null, name, flags, sig, args);
|
||||
else {
|
||||
if (null != ro.iface.getAnnotation(DBusInterfaceName.class)) {
|
||||
call = new MethodCall(ro.busname, ro.objectpath, ro.iface.getAnnotation(DBusInterfaceName.class).value(), name, flags, sig, args);
|
||||
} else
|
||||
call = new MethodCall(ro.busname, ro.objectpath, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), name, flags, sig, args);
|
||||
String name = DBusNamingUtil.getMethodName(_m);
|
||||
if (null == _ro.getInterface()) {
|
||||
call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), null, name, flags, sig, args);
|
||||
} else {
|
||||
String iface = DBusNamingUtil.getInterfaceName(_ro.getInterface());
|
||||
call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), iface, name, flags, sig, args);
|
||||
}
|
||||
} catch (DBusException DBe) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe);
|
||||
throw new DBusExecutionException(getString("constructOutgoingMethodCallFailure") + DBe.getMessage());
|
||||
} catch (DBusException _ex) {
|
||||
LOGGER.debug("Failed to construct outgoing method call.", _ex);
|
||||
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:
|
||||
conn.queueOutgoing(call);
|
||||
return new DBusAsyncReply(call, m, conn);
|
||||
_conn.sendMessage(call);
|
||||
return new DBusAsyncReply<>(call, _m, _conn);
|
||||
case CALL_TYPE_CALLBACK:
|
||||
synchronized (conn.pendingCallbacks) {
|
||||
if (Debug.debug) Debug.print(Debug.VERBOSE, "Queueing Callback " + callback + " for " + call);
|
||||
conn.pendingCallbacks.put(call, callback);
|
||||
conn.pendingCallbackReplys.put(call, new DBusAsyncReply(call, m, conn));
|
||||
}
|
||||
conn.queueOutgoing(call);
|
||||
_conn.queueCallback(call, _m, _callback);
|
||||
_conn.sendMessage(call);
|
||||
return null;
|
||||
case CALL_TYPE_SYNC:
|
||||
conn.queueOutgoing(call);
|
||||
_conn.sendMessage(call);
|
||||
break;
|
||||
}
|
||||
|
||||
// get reply
|
||||
if (m.isAnnotationPresent(DBus.Method.NoReply.class)) return null;
|
||||
if (_m.isAnnotationPresent(MethodNoReply.class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
try {
|
||||
return convertRV(reply.getSig(), reply.getParameters(), m, conn);
|
||||
} catch (DBusException e) {
|
||||
if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
|
||||
throw new DBusExecutionException(e.getMessage());
|
||||
return convertRV(reply.getSig(), reply.getParameters(), _m, _conn);
|
||||
} catch (DBusException _ex) {
|
||||
LOGGER.debug("", _ex);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
class RemoteObject {
|
||||
String busname;
|
||||
String objectpath;
|
||||
Class<? extends DBusInterface> iface;
|
||||
boolean autostart;
|
||||
import org.freedesktop.dbus.interfaces.DBusInterface;
|
||||
|
||||
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 class RemoteObject {
|
||||
private final String busname;
|
||||
private final String objectpath;
|
||||
private final Class<? extends DBusInterface> iface;
|
||||
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) {
|
||||
if (!(o instanceof RemoteObject)) return false;
|
||||
RemoteObject them = (RemoteObject) o;
|
||||
@Override
|
||||
public boolean equals(Object _o) {
|
||||
if (!(_o instanceof RemoteObject)) {
|
||||
return false;
|
||||
}
|
||||
RemoteObject them = (RemoteObject) _o;
|
||||
|
||||
if (!them.objectpath.equals(this.objectpath)) return false;
|
||||
|
||||
if (null == this.busname && null != them.busname) return false;
|
||||
if (null != this.busname && null == them.busname) return false;
|
||||
if (null != them.busname && !them.busname.equals(this.busname)) return false;
|
||||
|
||||
if (null == this.iface && null != them.iface) return false;
|
||||
if (null != this.iface && null == them.iface) return false;
|
||||
if (null != them.iface && !them.iface.equals(this.iface)) return false;
|
||||
if (!them.objectpath.equals(this.objectpath)) {
|
||||
return false;
|
||||
} else if (null == this.busname && null != them.busname) {
|
||||
return false;
|
||||
} else if (null != this.busname && null == them.busname) {
|
||||
return false;
|
||||
} else if (null != them.busname && !them.busname.equals(this.busname)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() +
|
||||
(null == iface ? 0 : iface.hashCode());
|
||||
return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + (null == iface ? 0 : iface.hashCode());
|
||||
}
|
||||
|
||||
public boolean autoStarting() {
|
||||
public boolean isAutostart() {
|
||||
return autostart;
|
||||
}
|
||||
|
||||
|
@ -61,6 +62,7 @@ class RemoteObject {
|
|||
return iface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return busname + ":" + objectpath + ":" + iface;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
class SignalTuple {
|
||||
String type;
|
||||
String name;
|
||||
String object;
|
||||
String source;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public SignalTuple(String type, String name, String object, String source) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.object = object;
|
||||
this.source = source;
|
||||
/**
|
||||
* @deprecated this class was used as map key internally and is no longer in use
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0 - 2022-08-18")
|
||||
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) {
|
||||
if (!(o instanceof SignalTuple)) return false;
|
||||
SignalTuple other = (SignalTuple) o;
|
||||
if (null == this.type && null != other.type) return false;
|
||||
if (null != this.type && !this.type.equals(other.type)) return false;
|
||||
if (null == this.name && null != other.name) return false;
|
||||
if (null != this.name && !this.name.equals(other.name)) return false;
|
||||
if (null == this.object && null != other.object) return false;
|
||||
if (null != this.object && !this.object.equals(other.object)) return false;
|
||||
if (null == this.source && null != other.source) return false;
|
||||
if (null != this.source && !this.source.equals(other.source)) return false;
|
||||
@Override
|
||||
public boolean equals(Object _o) {
|
||||
if (!(_o instanceof SignalTuple)) {
|
||||
return false;
|
||||
}
|
||||
SignalTuple other = (SignalTuple) _o;
|
||||
if (null == this.type && null != other.type) {
|
||||
return false;
|
||||
} else if (null != this.type && !this.type.equals(other.type)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (null == type ? 0 : type.hashCode())
|
||||
+ (null == name ? 0 : name.hashCode())
|
||||
+ (null == source ? 0 : source.hashCode())
|
||||
+ (null == object ? 0 : object.hashCode());
|
||||
return (null == type ? 0 : type.hashCode()) + (null == name ? 0 : name.hashCode()) + (null == source ? 0 : source.hashCode()) + (null == object ? 0 : object.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* An alternative to a WeakReference when you don't want
|
||||
* that behaviour.
|
||||
* that behavior.
|
||||
*/
|
||||
public class StrongReference<T> extends WeakReference<T> {
|
||||
T referant;
|
||||
private T referant;
|
||||
|
||||
public StrongReference(T referant) {
|
||||
super(referant);
|
||||
this.referant = referant;
|
||||
public StrongReference(T _referant) {
|
||||
super(_referant);
|
||||
this.referant = _referant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
referant = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enqueue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return referant;
|
||||
}
|
||||
|
||||
public boolean isEnqueued() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 + ".");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* This class should be extended to create Tuples.
|
||||
*
|
||||
* Any such class may be used as the return type for a method
|
||||
* which returns multiple values.
|
||||
*
|
||||
* 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
|
||||
* annotation, in the order they should appear to DBus.
|
||||
|
|
|
@ -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> {
|
||||
}
|
|
@ -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;
|
||||
|
||||
import org.freedesktop.dbus.exceptions.DBusException;
|
||||
|
@ -15,19 +5,21 @@ import org.freedesktop.dbus.exceptions.DBusException;
|
|||
import java.lang.reflect.Type;
|
||||
|
||||
public class TypeSignature {
|
||||
// CHECKSTYLE:OFF
|
||||
String sig;
|
||||
|
||||
public TypeSignature(String sig) {
|
||||
this.sig = sig;
|
||||
// CHECKSTYLE:ON
|
||||
public TypeSignature(String _sig) {
|
||||
this.sig = _sig;
|
||||
}
|
||||
|
||||
public TypeSignature(Type[] types) throws DBusException {
|
||||
public TypeSignature(Type[] _types) throws DBusException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (Type t : types) {
|
||||
for (Type t : _types) {
|
||||
String[] ts = Marshalling.getDBusType(t);
|
||||
for (String s : ts)
|
||||
for (String s : ts) {
|
||||
sb.append(s);
|
||||
}
|
||||
}
|
||||
this.sig = sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -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–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–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;
|
||||
}
|
||||
}
|
|
@ -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–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–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;
|
||||
}
|
||||
}
|
|
@ -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–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–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–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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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<Map<String, List<String>>> {
|
||||
* }
|
||||
* }
|
||||
* </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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.freedesktop.dbus.connections;
|
||||
|
||||
public interface IDisconnectAction {
|
||||
void perform();
|
||||
}
|
|
@ -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) {}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + ")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 > 1 may cause signals to be handled out-of-order
|
||||
* <p>
|
||||
* Default: 1
|
||||
*
|
||||
* @param _threads int >= 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 >= 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 > 1 to handle recursive calls.
|
||||
* <p>
|
||||
* Default: 4
|
||||
*
|
||||
* @param _threads int >= 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 >= 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 >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY}
|
||||
* @return this
|
||||
*
|
||||
* @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@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 >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY}
|
||||
* @return this
|
||||
*
|
||||
* @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@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 >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY}
|
||||
* @return this
|
||||
*
|
||||
* @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@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 >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY}
|
||||
* @return this
|
||||
*
|
||||
* @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 > 1 may cause signals to be handled out-of-order
|
||||
* <p>
|
||||
* Default: 1
|
||||
*
|
||||
* @param _threads int >= 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 >= 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 > 1 to handle recursive calls.
|
||||
* <p>
|
||||
* Default: 4
|
||||
*
|
||||
* @param _threads int >= 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 >= 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue