DefaultTunnel extends AbstractLifecycleObservable implements Tunnel { @Override public void release() { if(getState().equals(new TunnelClosed(this))) { logger.debug("already closed, no need to release again"); return; } setState(new TunnelClosed(this)); if(frontend != null) { frontend.release(); } if(backend != null) { backend.release(); } } DefaultTunnel(Channel frontendChannel, ProxyConnectProtocol protocol, ProxyConfig config,
                         ResourceManager resourceManager, TunnelMonitorManager tunnelMonitorManager); @Override TunnelIdentity identity(); @Override void forwardToBackend(ByteBuf message); @Override void forwardToFrontend(ByteBuf message); @Override FrontendSession frontend(); @Override BackendSession backend(); @Override TunnelMeta getTunnelMeta(); @Override TunnelState getState(); @Override ProxyConnectProtocol getProxyProtocol(); @Override TunnelMonitor getTunnelMonitor(); @Override void update(Object args, Observable observable); @Override int hashCode(); @Override void release();  }
@Test public void testRelease() throws Exception { tunnel.setState(new TunnelClosed(tunnel)); tunnel.release(); verify(frontend, never()).release(); verify(backend, never()).release(); }