blob: 349772669953ef89efbfca2b6a80337951aa1411 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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 com.intellij.cvsSupport2.connections.ssh;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.util.Consumer;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.Session;
import com.trilead.ssh2.StreamGobbler;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;
import org.netbeans.lib.cvsclient.connection.IConnection;
import org.netbeans.lib.cvsclient.io.IStreamLogger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
// logical connection, backed by SSH Session
public class SshSessionConnection implements IConnection {
private volatile long myTs;
private final String myRepository;
private final Consumer<SshSessionConnection> myCloseListener;
private final ThrowableComputable<Session, AuthenticationException> mySessionProvider;
private volatile LifeStages myState;
private Session mySession;
private InputStream myInputStream;
private OutputStream myOutputStream;
private final Runnable myActivityMonitor;
private StreamGobbler myErrorStreamGobbler;
public SshSessionConnection(final String repository, final Consumer<SshSessionConnection> closeListener,
final ThrowableComputable<Session, AuthenticationException> sessionProvider) {
myRepository = repository;
myCloseListener = closeListener;
mySessionProvider = sessionProvider;
myTs = System.currentTimeMillis();
myActivityMonitor = new Runnable() {
public void run() {
myTs = System.currentTimeMillis();
}
};
myState = LifeStages.NOT_EXIST;
}
public InputStream getInputStream() {
return myInputStream;
}
public OutputStream getOutputStream() {
return myOutputStream;
}
public String getRepository() {
return myRepository;
}
public void verify(IStreamLogger streamLogger) throws AuthenticationException {
}
public void open(IStreamLogger streamLogger) throws AuthenticationException {
SshLogger.debug("opening session");
mySession = mySessionProvider.compute();
// wrapper created, inspection is inapplicable
//noinspection IOResourceOpenedButNotSafelyClosed
myInputStream = new MyInputStreamWrapper(myActivityMonitor, mySession.getStdout());
// wrapper created, inspection is inapplicable
//noinspection IOResourceOpenedButNotSafelyClosed
myOutputStream = new MyOutputStreamWrapper(myActivityMonitor, mySession.getStdin());
myErrorStreamGobbler = new StreamGobbler(mySession.getStderr());
myState = LifeStages.CREATED;
}
public void close() throws IOException {
myState = LifeStages.CLOSING;
SshLogger.debug("session set to closing; closing streams...");
try {
if (myInputStream != null) {
try {
myInputStream.close();
}
catch (IOException e) {
//
}
}
if (myOutputStream != null) {
try {
myOutputStream.close();
}
catch (IOException e) {
//
}
}
if (myErrorStreamGobbler != null) {
try {
myErrorStreamGobbler.close();
}
catch (IOException e) {
//
}
SshLogger.debug("session itself to be closed");
mySession.close();
mySession.waitForCondition(ChannelCondition.CLOSED, 0);
}
} finally {
SshLogger.debug("session closed, notifying connection");
myCloseListener.consume(this);
myState = LifeStages.CLOSED;
}
}
public long getTs() {
return myTs;
}
public LifeStages getState() {
return myState;
}
private static class MyInputStreamWrapper extends InputStream {
private final Runnable myListener;
private final InputStream myDelegate;
private MyInputStreamWrapper(final Runnable listener, final InputStream delegate) {
myListener = listener;
myDelegate = delegate;
}
@Override
public int read() throws IOException {
myListener.run();
return myDelegate.read();
}
@Override
public int read(byte[] b) throws IOException {
myListener.run();
return myDelegate.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
myListener.run();
return myDelegate.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return myDelegate.skip(n);
}
@Override
public int available() throws IOException {
return myDelegate.available();
}
@Override
public void close() throws IOException {
myDelegate.close();
}
@Override
public void mark(int readlimit) {
myDelegate.mark(readlimit);
}
@Override
public void reset() throws IOException {
myDelegate.reset();
}
@Override
public boolean markSupported() {
return myDelegate.markSupported();
}
}
private static class MyOutputStreamWrapper extends OutputStream {
private final Runnable myListener;
private final OutputStream myDelegate;
private MyOutputStreamWrapper(final Runnable listener, final OutputStream delegate) {
myListener = listener;
myDelegate = delegate;
}
@Override
public void write(int b) throws IOException {
myListener.run();
myDelegate.write(b);
}
@Override
public void write(byte[] b) throws IOException {
myListener.run();
myDelegate.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
myListener.run();
myDelegate.write(b, off, len);
}
@Override
public void flush() throws IOException {
myDelegate.flush();
}
@Override
public void close() throws IOException {
myDelegate.close();
}
}
}