static { // dead-lock prevention. // // creating a new proxy class is a classloading activity, so it can create a dead-lock situation // if thread A starts classloading via RemoteClassLoader.ladClass(), // then thread B use JarCacheSupport.prefetch and tries to create a proxy for JarLoader // (which blocks as Proxy.getProxyClass waits for RemoteClassLoader.defineClass lock by // thread A) // then thread A tries to touch JarLoader proxy (which blocks on thread B) // // to avoid situations like this, create proxy classes that we need during the classloading jarLoaderProxy = RemoteInvocationHandler.getProxyClass(JarLoader.class); }
/** * Creates a new channel. * * @param name See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, * OutputStream, boolean, ClassLoader)} * @param exec See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, * OutputStream, boolean, ClassLoader)} * @param transport The transport that we run {@link Channel} on top of. * @param base See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, * OutputStream, boolean, ClassLoader)} * @param restricted See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, * OutputStream, boolean, ClassLoader)} * @since 2.13 */ public Channel( String name, ExecutorService exec, CommandTransport transport, boolean restricted, ClassLoader base) throws IOException { this.name = name; this.executor = new InterceptingExecutorService(exec); this.isRestricted = restricted; this.underlyingOutput = transport.getUnderlyingStream(); if (base == null) base = getClass().getClassLoader(); this.baseClassLoader = base; if (export(this, false) != 1) throw new AssertionError(); // export number 1 is reserved for the channel itself remoteChannel = RemoteInvocationHandler.wrap(this, 1, IChannel.class, true, false); this.remoteCapability = transport.getRemoteCapability(); this.pipeWriter = new PipeWriter(createPipeWriterExecutor()); this.transport = transport; transport.setup( this, new CommandReceiver() { public void handle(Command cmd) { lastHeard = System.currentTimeMillis(); if (logger.isLoggable(Level.FINE)) logger.fine("Received " + cmd); try { cmd.execute(Channel.this); } catch (Throwable t) { logger.log( Level.SEVERE, "Failed to execute command " + cmd + " (channel " + Channel.this.name + ")", t); logger.log(Level.SEVERE, "This command is created here", cmd.createdAt); } } public void terminate(IOException e) { Channel.this.terminate(e); } }); }
/*package*/ Channel(ChannelBuilder settings, CommandTransport transport) throws IOException { this.name = settings.getName(); this.executor = new InterceptingExecutorService(settings.getExecutors()); this.isRestricted = settings.isRestricted(); this.underlyingOutput = transport.getUnderlyingStream(); this.jarCache = settings.getJarCache(); this.baseClassLoader = settings.getBaseLoader(); if (export(this, false) != 1) throw new AssertionError(); // export number 1 is reserved for the channel itself remoteChannel = RemoteInvocationHandler.wrap(this, 1, IChannel.class, true, false); this.remoteCapability = transport.getRemoteCapability(); this.pipeWriter = new PipeWriter(createPipeWriterExecutor()); this.transport = transport; this.jarLoader = new JarLoaderImpl(); // TODO: figure out a mechanism to allow the user to share this across // Channels setProperty(JarLoader.OURS, export(JarLoader.class, jarLoader, false)); transport.setup( this, new CommandReceiver() { public void handle(Command cmd) { lastHeard = System.currentTimeMillis(); if (logger.isLoggable(Level.FINE)) logger.fine("Received " + cmd); try { cmd.execute(Channel.this); } catch (Throwable t) { logger.log( Level.SEVERE, "Failed to execute command " + cmd + " (channel " + Channel.this.name + ")", t); logger.log(Level.SEVERE, "This command is created here", cmd.createdAt); } } public void terminate(IOException e) { Channel.this.terminate(e); } }); }
/** * @param userProxy If true, the returned proxy will be capable to handle classes defined in the * user classloader as parameters and return values. Such proxy relies on {@link * RemoteClassLoader} and related mechanism, so it's not usable for implementing lower-layer * services that are used by {@link RemoteClassLoader}. * <p>To create proxies for objects inside remoting, pass in false. */ /*package*/ <T> T export(Class<T> type, T instance, boolean userProxy) { if (instance == null) return null; // every so often perform GC on the remote system so that // unused RemoteInvocationHandler get released, which triggers // unexport operation. if ((++gcCounter) % 10000 == 0) try { send(new GCCommand()); } catch (IOException e) { // for compatibility reason we can't change the export method signature logger.log(Level.WARNING, "Unable to send GC command", e); } // either local side will auto-unexport, or the remote side will unexport when it's GC-ed boolean autoUnexportByCaller = exportedObjects.isRecording(); final int id = export(instance, autoUnexportByCaller); return RemoteInvocationHandler.wrap(null, id, type, userProxy, autoUnexportByCaller); }
@SuppressWarnings("unchecked") private synchronized void checkReply() { if (mc.hasReply()) { Message m = mc.getReply(); if (m instanceof Error) error = ((Error) m).getException(); 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()); } } } }