You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/04/25 05:01:27 UTC

[6/8] mina-sshd git commit: [SSHD-818] Split SCP code (client + server) to its own module

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/common/scp/helpers/LocalFileScpTargetStreamResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/helpers/LocalFileScpTargetStreamResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/helpers/LocalFileScpTargetStreamResolver.java
deleted file mode 100644
index 6b57443..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/helpers/LocalFileScpTargetStreamResolver.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.common.scp.helpers;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.nio.file.AccessDeniedException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.scp.ScpFileOpener;
-import org.apache.sshd.common.scp.ScpTargetStreamResolver;
-import org.apache.sshd.common.scp.ScpTimestamp;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LocalFileScpTargetStreamResolver extends AbstractLoggingBean implements ScpTargetStreamResolver {
-    protected final Path path;
-    protected final ScpFileOpener opener;
-    protected final Boolean status;
-    private Path file;
-
-    public LocalFileScpTargetStreamResolver(Path path, ScpFileOpener opener) throws IOException {
-        LinkOption[] linkOptions = IoUtils.getLinkOptions(true);
-        this.status = IoUtils.checkFileExists(path, linkOptions);
-        if (status == null) {
-            throw new AccessDeniedException("Receive target file path existence status cannot be determined: " + path);
-        }
-
-        this.path = path;
-        this.opener = (opener == null) ? DefaultScpFileOpener.INSTANCE : opener;
-    }
-
-    @Override
-    public OutputStream resolveTargetStream(Session session, String name, long length,
-            Set<PosixFilePermission> perms, OpenOption... options) throws IOException {
-        if (file != null) {
-            throw new StreamCorruptedException("resolveTargetStream(" + name + ")[" + perms + "] already resolved: " + file);
-        }
-
-        LinkOption[] linkOptions = IoUtils.getLinkOptions(true);
-        if (status && Files.isDirectory(path, linkOptions)) {
-            String localName = name.replace('/', File.separatorChar);   // in case we are running on Windows
-            file = path.resolve(localName);
-        } else if (status && Files.isRegularFile(path, linkOptions)) {
-            file = path;
-        } else if (!status) {
-            Path parent = path.getParent();
-
-            Boolean parentStatus = IoUtils.checkFileExists(parent, linkOptions);
-            if (parentStatus == null) {
-                throw new AccessDeniedException("Receive file parent (" + parent + ") existence status cannot be determined for " + path);
-            }
-
-            if (parentStatus && Files.isDirectory(parent, linkOptions)) {
-                file = path;
-            }
-        }
-
-        if (file == null) {
-            throw new IOException("Can not write to " + path);
-        }
-
-        Boolean fileStatus = IoUtils.checkFileExists(file, linkOptions);
-        if (fileStatus == null) {
-            throw new AccessDeniedException("Receive file existence status cannot be determined: " + file);
-        }
-
-        if (fileStatus) {
-            if (Files.isDirectory(file, linkOptions)) {
-                throw new IOException("File is a directory: " + file);
-            }
-
-            if (!Files.isWritable(file)) {
-                throw new IOException("Can not write to file: " + file);
-            }
-        }
-
-        if (log.isTraceEnabled()) {
-            log.trace("resolveTargetStream(" + name + "): " + file);
-        }
-
-        return opener.openWrite(session, file, options);
-    }
-
-    @Override
-    public Path getEventListenerFilePath() {
-        if (file == null) {
-            return path;
-        } else {
-            return file;
-        }
-    }
-
-    @Override
-    public void postProcessReceivedData(String name, boolean preserve, Set<PosixFilePermission> perms, ScpTimestamp time) throws IOException {
-        if (file == null) {
-            throw new StreamCorruptedException("postProcessReceivedData(" + name + ")[" + perms + "] No currently resolved data");
-        }
-
-        if (preserve) {
-            updateFileProperties(name, file, perms, time);
-        }
-    }
-
-    protected void updateFileProperties(String name, Path path, Set<PosixFilePermission> perms, ScpTimestamp time) throws IOException {
-        boolean traceEnabled = log.isTraceEnabled();
-        if (traceEnabled) {
-            log.trace("updateFileProperties(" + name + ")[" + path + "] permissions: " + perms);
-        }
-        IoUtils.setPermissions(path, perms);
-
-        if (time != null) {
-            BasicFileAttributeView view = Files.getFileAttributeView(path, BasicFileAttributeView.class);
-            FileTime lastModified = FileTime.from(time.getLastModifiedTime(), TimeUnit.MILLISECONDS);
-            FileTime lastAccess = FileTime.from(time.getLastAccessTime(), TimeUnit.MILLISECONDS);
-            if (traceEnabled) {
-                log.trace("updateFileProperties(" + name + ")[" + path + "] last-modified=" + lastModified + ", last-access=" + lastAccess);
-            }
-
-            view.setTimes(lastModified, lastAccess, null);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return String.valueOf(getEventListenerFilePath());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
new file mode 100644
index 0000000..a55ac2e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.util.io.functors;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Invokes some I/O function on the input returning some output
+ * and potentially throwing an {@link IOException} in the process
+ *
+ * @param <T> Type of input
+ * @param <R> Type of output
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface IOFunction<T, R> {
+    R apply(T t) throws IOException;
+
+    /**
+     * Returns a composed function that first applies the {@code before}
+     * function to its input, and then applies this function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of input to the {@code before} function, and to the
+     *           composed function
+     * @param before the function to apply before this function is applied
+     * @return a composed function that first applies the {@code before}
+     * function and then applies this function
+     * @throws NullPointerException if before is null
+     *
+     * @see #andThen(IOFunction)
+     */
+    default <V> IOFunction<V, R> compose(IOFunction<? super V, ? extends T> before) {
+        Objects.requireNonNull(before, "No composing function provided");
+        return (V v) -> apply(before.apply(v));
+    }
+
+    /**
+     * Returns a composed function that first applies this function to
+     * its input, and then applies the {@code after} function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of output of the {@code after} function, and of the
+     *           composed function
+     * @param after the function to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} function
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
+        Objects.requireNonNull(after, "No composing function provided");
+        return (T t) -> after.apply(apply(t));
+    }
+
+    /**
+     * Returns a function that always returns its input argument.
+     *
+     * @param <T> the type of the input and output objects to the function
+     * @return a function that always returns its input argument
+     */
+    static <T> IOFunction<T, T> identity() {
+        return t -> t;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
deleted file mode 100644
index e80f791..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.server.scp;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.FileSystem;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.scp.ScpException;
-import org.apache.sshd.common.scp.ScpFileOpener;
-import org.apache.sshd.common.scp.ScpHelper;
-import org.apache.sshd.common.scp.ScpTransferEventListener;
-import org.apache.sshd.common.scp.helpers.DefaultScpFileOpener;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionHolder;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-import org.apache.sshd.server.SessionAware;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.session.ServerSessionHolder;
-
-/**
- * This commands provide SCP support on both server and client side.
- * Permissions and preservation of access / modification times on files
- * are not supported.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ScpCommand
-        extends AbstractLoggingBean
-        implements Command, Runnable, FileSystemAware, SessionAware,
-                   SessionHolder<Session>, ServerSessionHolder, ExecutorServiceCarrier {
-
-    protected final String name;
-    protected final int sendBufferSize;
-    protected final int receiveBufferSize;
-    protected final ScpFileOpener opener;
-    protected boolean optR;
-    protected boolean optT;
-    protected boolean optF;
-    protected boolean optD;
-    protected boolean optP; // TODO: handle modification times
-    protected FileSystem fileSystem;
-    protected String path;
-    protected InputStream in;
-    protected OutputStream out;
-    protected OutputStream err;
-    protected ExitCallback callback;
-    protected IOException error;
-    protected Future<?> pendingFuture;
-    protected ScpTransferEventListener listener;
-    protected ServerSession serverSession;
-
-    private ExecutorService executorService;
-    private boolean shutdownOnExit;
-
-    /**
-     * @param command         The command to be executed
-     * @param executorService An {@link ExecutorService} to be used when
-     *                        {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
-     *                        single-threaded service is created and used.
-     * @param shutdownOnExit  If {@code true} the {@link ExecutorService#shutdownNow()}
-     *                        will be called when command terminates - unless it is the ad-hoc
-     *                        service, which will be shutdown regardless
-     * @param sendSize        Size (in bytes) of buffer to use when sending files
-     * @param receiveSize     Size (in bytes) of buffer to use when receiving files
-     * @param fileOpener      The {@link ScpFileOpener} - if {@code null} then {@link DefaultScpFileOpener} is used
-     * @param eventListener   An {@link ScpTransferEventListener} - may be {@code null}
-     * @see ThreadUtils#newSingleThreadExecutor(String)
-     * @see ScpHelper#MIN_SEND_BUFFER_SIZE
-     * @see ScpHelper#MIN_RECEIVE_BUFFER_SIZE
-     */
-    public ScpCommand(String command,
-            ExecutorService executorService, boolean shutdownOnExit,
-            int sendSize, int receiveSize,
-            ScpFileOpener fileOpener, ScpTransferEventListener eventListener) {
-        name = command;
-
-        if (executorService == null) {
-            String poolName = command.replace(' ', '_').replace('/', ':');
-            this.executorService = ThreadUtils.newSingleThreadExecutor(poolName);
-            this.shutdownOnExit = true;    // we always close the ad-hoc executor service
-        } else {
-            this.executorService = executorService;
-            this.shutdownOnExit = shutdownOnExit;
-        }
-
-        if (sendSize < ScpHelper.MIN_SEND_BUFFER_SIZE) {
-            throw new IllegalArgumentException("<ScpCommmand>(" + command + ") send buffer size "
-                    + "(" + sendSize + ") below minimum required "
-                    + "(" + ScpHelper.MIN_SEND_BUFFER_SIZE + ")");
-        }
-        sendBufferSize = sendSize;
-
-        if (receiveSize < ScpHelper.MIN_RECEIVE_BUFFER_SIZE) {
-            throw new IllegalArgumentException("<ScpCommmand>(" + command + ") receive buffer size "
-                    + "(" + sendSize + ") below minimum required "
-                    + "(" + ScpHelper.MIN_RECEIVE_BUFFER_SIZE + ")");
-        }
-        receiveBufferSize = receiveSize;
-
-        opener = (fileOpener == null) ? DefaultScpFileOpener.INSTANCE : fileOpener;
-        listener = (eventListener == null) ? ScpTransferEventListener.EMPTY : eventListener;
-
-        boolean debugEnabled = log.isDebugEnabled();
-        if (debugEnabled) {
-            log.debug("Executing command {}", command);
-        }
-
-        String[] args = GenericUtils.split(command, ' ');
-        int numArgs = GenericUtils.length(args);
-        for (int i = 1; i < numArgs; i++) {
-            String argVal = args[i];
-            if (argVal.charAt(0) == '-') {
-                for (int j = 1; j < argVal.length(); j++) {
-                    char option = argVal.charAt(j);
-                    switch (option) {
-                        case 'f':
-                            optF = true;
-                            break;
-                        case 'p':
-                            optP = true;
-                            break;
-                        case 'r':
-                            optR = true;
-                            break;
-                        case 't':
-                            optT = true;
-                            break;
-                        case 'd':
-                            optD = true;
-                            break;
-                        default:  // ignored
-                            if (debugEnabled) {
-                                log.debug("Unknown flag ('{}') in command={}", option, command);
-                            }
-                    }
-                }
-            } else {
-                String prevArg = args[i - 1];
-                path = command.substring(command.indexOf(prevArg) + prevArg.length() + 1);
-
-                int pathLen = path.length();
-                char startDelim = path.charAt(0);
-                char endDelim = (pathLen > 2) ? path.charAt(pathLen - 1) : '\0';
-                // remove quotes
-                if ((pathLen > 2) && (startDelim == endDelim) && ((startDelim == '\'') || (startDelim == '"'))) {
-                    path = path.substring(1, pathLen - 1);
-                }
-                break;
-            }
-        }
-
-        if ((!optF) && (!optT)) {
-            error = new IOException("Either -f or -t option should be set for " + command);
-        }
-    }
-
-    @Override
-    public ExecutorService getExecutorService() {
-        return executorService;
-    }
-
-    @Override
-    public boolean isShutdownOnExit() {
-        return shutdownOnExit;
-    }
-
-    @Override
-    public Session getSession() {
-        return getServerSession();
-    }
-
-    @Override
-    public ServerSession getServerSession() {
-        return serverSession;
-    }
-
-    @Override
-    public void setSession(ServerSession session) {
-        serverSession = session;
-    }
-
-    @Override
-    public void setInputStream(InputStream in) {
-        this.in = in;
-    }
-
-    @Override
-    public void setOutputStream(OutputStream out) {
-        this.out = out;
-    }
-
-    @Override
-    public void setErrorStream(OutputStream err) {
-        this.err = err;
-    }
-
-    @Override
-    public void setExitCallback(ExitCallback callback) {
-        this.callback = callback;
-    }
-
-    @Override
-    public void setFileSystem(FileSystem fs) {
-        this.fileSystem = fs;
-    }
-
-    @Override
-    public void start(Environment env) throws IOException {
-        if (error != null) {
-            throw error;
-        }
-
-        try {
-            ExecutorService executors = getExecutorService();
-            pendingFuture = executors.submit(this);
-        } catch (RuntimeException e) {    // e.g., RejectedExecutionException
-            log.error("Failed (" + e.getClass().getSimpleName() + ") to start command=" + name + ": " + e.getMessage(), e);
-            throw new IOException(e);
-        }
-    }
-
-    @Override
-    public void destroy() {
-        // if thread has not completed, cancel it
-        boolean debugEnabled = log.isDebugEnabled();
-        if ((pendingFuture != null) && (!pendingFuture.isDone())) {
-            boolean result = pendingFuture.cancel(true);
-            // TODO consider waiting some reasonable (?) amount of time for cancellation
-            if (debugEnabled) {
-                log.debug("destroy() - cancel pending future=" + result);
-            }
-        }
-
-        pendingFuture = null;
-
-        ExecutorService executors = getExecutorService();
-        if ((executors != null) && (!executors.isShutdown()) && isShutdownOnExit()) {
-            Collection<Runnable> runners = executors.shutdownNow();
-            if (debugEnabled) {
-                log.debug("destroy() - shutdown executor service - runners count=" + runners.size());
-            }
-        }
-        this.executorService = null;
-
-        try {
-            fileSystem.close();
-        } catch (UnsupportedOperationException e) {
-            // Ignore
-        } catch (IOException e) {
-            log.debug("Error closing FileSystem", e);
-        }
-    }
-
-    @Override
-    public void run() {
-        int exitValue = ScpHelper.OK;
-        String exitMessage = null;
-        ScpHelper helper = new ScpHelper(getServerSession(), in, out, fileSystem, opener, listener);
-        try {
-            if (optT) {
-                helper.receive(helper.resolveLocalPath(path), optR, optD, optP, receiveBufferSize);
-            } else if (optF) {
-                helper.send(Collections.singletonList(path), optR, optP, sendBufferSize);
-            } else {
-                throw new IOException("Unsupported mode");
-            }
-        } catch (IOException e) {
-            ServerSession session = getServerSession();
-            boolean debugEnabled = log.isDebugEnabled();
-            try {
-                Integer statusCode = null;
-                if (e instanceof ScpException) {
-                    statusCode = ((ScpException) e).getExitStatus();
-                }
-                exitValue = (statusCode == null) ? ScpHelper.ERROR : statusCode;
-                // this is an exception so status cannot be OK/WARNING
-                if ((exitValue == ScpHelper.OK) || (exitValue == ScpHelper.WARNING)) {
-                    if (debugEnabled) {
-                        log.debug("run({})[{}] normalize status code={}", session, name, exitValue);
-                    }
-                    exitValue = ScpHelper.ERROR;
-                }
-                exitMessage = GenericUtils.trimToEmpty(e.getMessage());
-                writeCommandResponseMessage(name, exitValue, exitMessage);
-            } catch (IOException e2) {
-                if (debugEnabled) {
-                    log.debug("run({})[{}] Failed ({}) to send error response: {}",
-                              session, name, e.getClass().getSimpleName(), e.getMessage());
-                }
-                if (log.isTraceEnabled()) {
-                    log.trace("run(" + session + ")[" + name + "] error response failure details", e2);
-                }
-            }
-
-            if (debugEnabled) {
-                log.debug("run({})[{}] Failed ({}) to run command: {}",
-                          session, name, e.getClass().getSimpleName(), e.getMessage());
-            }
-            if (log.isTraceEnabled()) {
-                log.trace("run(" + session + ")[" + name + "] command execution failure details", e);
-            }
-        } finally {
-            if (callback != null) {
-                callback.onExit(exitValue, GenericUtils.trimToEmpty(exitMessage));
-            }
-        }
-    }
-
-    protected void writeCommandResponseMessage(String command, int exitValue, String exitMessage) throws IOException {
-        if (log.isDebugEnabled()) {
-            log.debug("writeCommandResponseMessage({}) command='{}', exit-status={}: {}",
-                      getServerSession(), command, exitValue, exitMessage);
-        }
-        ScpHelper.sendResponseMessage(out, exitValue, exitMessage);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "(" + getSession() + ") " + name;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommandFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommandFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommandFactory.java
deleted file mode 100644
index 99e3e34..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommandFactory.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.server.scp;
-
-import java.util.Collection;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.common.scp.ScpFileOpener;
-import org.apache.sshd.common.scp.ScpFileOpenerHolder;
-import org.apache.sshd.common.scp.ScpHelper;
-import org.apache.sshd.common.scp.ScpTransferEventListener;
-import org.apache.sshd.common.util.EventListenerUtils;
-import org.apache.sshd.common.util.ObjectBuilder;
-import org.apache.sshd.common.util.threads.ExecutorServiceConfigurer;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.CommandFactory;
-
-/**
- * This <code>CommandFactory</code> can be used as a standalone command factory
- * or can be used to augment another <code>CommandFactory</code> and provides
- * <code>SCP</code> support.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see ScpCommand
- */
-public class ScpCommandFactory
-        implements ScpFileOpenerHolder,
-        CommandFactory,
-        Cloneable,
-        ExecutorServiceConfigurer {
-    /**
-     * A useful {@link ObjectBuilder} for {@link ScpCommandFactory}
-     */
-    public static class Builder implements ObjectBuilder<ScpCommandFactory> {
-        private final ScpCommandFactory factory = new ScpCommandFactory();
-
-        public Builder() {
-            super();
-        }
-
-        public Builder withFileOpener(ScpFileOpener opener) {
-            factory.setScpFileOpener(opener);
-            return this;
-        }
-
-        public Builder withDelegate(CommandFactory delegate) {
-            factory.setDelegateCommandFactory(delegate);
-            return this;
-        }
-
-        public Builder withExecutorService(ExecutorService service) {
-            factory.setExecutorService(service);
-            return this;
-        }
-
-        public Builder withShutdownOnExit(boolean shutdown) {
-            factory.setShutdownOnExit(shutdown);
-            return this;
-        }
-
-        public Builder withSendBufferSize(int sendSize) {
-            factory.setSendBufferSize(sendSize);
-            return this;
-        }
-
-        public Builder withReceiveBufferSize(int receiveSize) {
-            factory.setReceiveBufferSize(receiveSize);
-            return this;
-        }
-
-        public Builder addEventListener(ScpTransferEventListener listener) {
-            factory.addEventListener(listener);
-            return this;
-        }
-
-        public Builder removeEventListener(ScpTransferEventListener listener) {
-            factory.removeEventListener(listener);
-            return this;
-        }
-
-        @Override
-        public ScpCommandFactory build() {
-            return factory.clone();
-        }
-    }
-
-    /*
-     * NOTE: we expose setters since there is no problem to change these settings between
-     * successive invocations of the 'createCommand' method
-     */
-    private CommandFactory delegate;
-    private ExecutorService executors;
-    private boolean shutdownExecutor;
-    private ScpFileOpener fileOpener;
-    private int sendBufferSize = ScpHelper.MIN_SEND_BUFFER_SIZE;
-    private int receiveBufferSize = ScpHelper.MIN_RECEIVE_BUFFER_SIZE;
-    private Collection<ScpTransferEventListener> listeners = new CopyOnWriteArraySet<>();
-    private ScpTransferEventListener listenerProxy;
-
-    public ScpCommandFactory() {
-        listenerProxy = EventListenerUtils.proxyWrapper(ScpTransferEventListener.class, getClass().getClassLoader(), listeners);
-    }
-
-    @Override
-    public ScpFileOpener getScpFileOpener() {
-        return fileOpener;
-    }
-
-    @Override
-    public void setScpFileOpener(ScpFileOpener fileOpener) {
-        this.fileOpener = fileOpener;
-    }
-
-    public CommandFactory getDelegateCommandFactory() {
-        return delegate;
-    }
-
-    /**
-     * @param factory A {@link CommandFactory} to be used if the
-     * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
-     * will be thrown when attempting to invoke {@link #createCommand(String)}
-     * with a non-SCP command
-     */
-    public void setDelegateCommandFactory(CommandFactory factory) {
-        delegate = factory;
-    }
-
-    @Override
-    public ExecutorService getExecutorService() {
-        return executors;
-    }
-
-    /**
-     * @param service An {@link ExecutorService} to be used when
-     * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
-     * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
-     * when the command is terminated - unless it is the ad-hoc service, which will be
-     * shutdown regardless
-     */
-    @Override
-    public void setExecutorService(ExecutorService service) {
-        executors = service;
-    }
-
-    @Override
-    public boolean isShutdownOnExit() {
-        return shutdownExecutor;
-    }
-
-    @Override
-    public void setShutdownOnExit(boolean shutdown) {
-        shutdownExecutor = shutdown;
-    }
-
-    public int getSendBufferSize() {
-        return sendBufferSize;
-    }
-
-    /**
-     * @param sendSize Size (in bytes) of buffer to use when sending files
-     * @see ScpHelper#MIN_SEND_BUFFER_SIZE
-     */
-    public void setSendBufferSize(int sendSize) {
-        if (sendSize < ScpHelper.MIN_SEND_BUFFER_SIZE) {
-            throw new IllegalArgumentException("<ScpCommandFactory>() send buffer size "
-                    + "(" + sendSize + ") below minimum required (" + ScpHelper.MIN_SEND_BUFFER_SIZE + ")");
-        }
-        sendBufferSize = sendSize;
-    }
-
-    public int getReceiveBufferSize() {
-        return receiveBufferSize;
-    }
-
-    /**
-     * @param receiveSize Size (in bytes) of buffer to use when receiving files
-     * @see ScpHelper#MIN_RECEIVE_BUFFER_SIZE
-     */
-    public void setReceiveBufferSize(int receiveSize) {
-        if (receiveSize < ScpHelper.MIN_RECEIVE_BUFFER_SIZE) {
-            throw new IllegalArgumentException("<ScpCommandFactory>() receive buffer size "
-                    + "(" + receiveSize + ") below minimum required (" + ScpHelper.MIN_RECEIVE_BUFFER_SIZE + ")");
-        }
-        receiveBufferSize = receiveSize;
-    }
-
-    /**
-     * @param listener The {@link ScpTransferEventListener} to add
-     * @return {@code true} if this is a <U>new</U> listener instance,
-     * {@code false} if the listener is already registered
-     * @throws IllegalArgumentException if {@code null} listener
-     */
-    public boolean addEventListener(ScpTransferEventListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("No listener instance");
-        }
-
-        return listeners.add(listener);
-    }
-
-    /**
-     * @param listener The {@link ScpTransferEventListener} to remove
-     * @return {@code true} if the listener was registered and removed,
-     * {@code false} if the listener was not registered to begin with
-     * @throws IllegalArgumentException if {@code null} listener
-     */
-    public boolean removeEventListener(ScpTransferEventListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("No listener instance");
-        }
-
-        return listeners.remove(listener);
-    }
-
-    /**
-     * Parses a command string and verifies that the basic syntax is
-     * correct. If parsing fails the responsibility is delegated to
-     * the configured {@link CommandFactory} instance; if one exist.
-     *
-     * @param command command to parse
-     * @return configured {@link Command} instance
-     * @throws IllegalArgumentException if not an SCP command and no
-     *                                  delegate command factory is available
-     * @see ScpHelper#SCP_COMMAND_PREFIX
-     */
-    @Override
-    public Command createCommand(String command) {
-        if (command.startsWith(ScpHelper.SCP_COMMAND_PREFIX)) {
-            return new ScpCommand(command,
-                    getExecutorService(), isShutdownOnExit(),
-                    getSendBufferSize(), getReceiveBufferSize(),
-                    getScpFileOpener(), listenerProxy);
-        }
-
-        CommandFactory factory = getDelegateCommandFactory();
-        if (factory != null) {
-            return factory.createCommand(command);
-        }
-
-        throw new IllegalArgumentException("Unknown command, does not begin with '" + ScpHelper.SCP_COMMAND_PREFIX + "': " + command);
-    }
-
-    @Override
-    public ScpCommandFactory clone() {
-        try {
-            ScpCommandFactory other = getClass().cast(super.clone());
-            // clone the listeners set as well
-            other.listeners = new CopyOnWriteArraySet<>(this.listeners);
-            other.listenerProxy = EventListenerUtils.proxyWrapper(ScpTransferEventListener.class, getClass().getClassLoader(), other.listeners);
-            return other;
-        } catch (CloneNotSupportedException e) {
-            throw new RuntimeException(e);    // un-expected...
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/server/scp/UnknownCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/scp/UnknownCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/scp/UnknownCommand.java
deleted file mode 100644
index d847291..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/scp/UnknownCommand.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.server.scp;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-
-/**
- * Implementation of an unknown command that can be returned by <code>CommandFactory</code>
- * when the command is not known, as it is supposed to always
- * return a valid <code>Command</code> object.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UnknownCommand implements Command, Runnable {
-
-    private final String command;
-    private final String message;
-    @SuppressWarnings("unused")
-    private InputStream in;
-    @SuppressWarnings("unused")
-    private OutputStream out;
-    private OutputStream err;
-    private ExitCallback callback;
-
-    public UnknownCommand(String command) {
-        this.command = ValidateUtils.checkNotNullAndNotEmpty(command, "No command");
-        this.message = "Unknown command: " + command;
-    }
-
-    public String getCommand() {
-        return command;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-
-    @Override
-    public void setInputStream(InputStream in) {
-        this.in = in;
-    }
-
-    @Override
-    public void setOutputStream(OutputStream out) {
-        this.out = out;
-    }
-
-    @Override
-    public void setErrorStream(OutputStream err) {
-        this.err = err;
-    }
-
-    @Override
-    public void setExitCallback(ExitCallback callback) {
-        this.callback = callback;
-    }
-
-    @Override
-    public void run() {
-        String errorMessage = getMessage();
-        try {
-            try {
-                err.write(errorMessage.getBytes(StandardCharsets.UTF_8));
-                err.write('\n');
-            } finally {
-                err.flush();
-            }
-        } catch (IOException e) {
-            // ignored
-        }
-
-        if (callback != null) {
-            callback.onExit(1, errorMessage);
-        }
-    }
-
-    @Override
-    public void start(Environment env) throws IOException {
-        Thread thread = new Thread(this);
-        thread.setDaemon(true);
-        thread.start();
-    }
-
-    @Override
-    public void destroy() {
-        // ignored
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(getCommand());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (obj == this) {
-            return true;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        return Objects.equals(this.getCommand(), ((UnknownCommand) obj).getCommand());
-    }
-
-    @Override
-    public String toString() {
-        return getMessage();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommand.java
new file mode 100644
index 0000000..59d970c
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommand.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.server.shell;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+
+/**
+ * Implementation of an unknown command that can be returned by <code>CommandFactory</code>
+ * when the command is not known, as it is supposed to always
+ * return a valid <code>Command</code> object.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UnknownCommand implements Command, Runnable {
+
+    private final String command;
+    private final String message;
+    @SuppressWarnings("unused")
+    private InputStream in;
+    @SuppressWarnings("unused")
+    private OutputStream out;
+    private OutputStream err;
+    private ExitCallback callback;
+
+    public UnknownCommand(String command) {
+        this.command = ValidateUtils.checkNotNullAndNotEmpty(command, "No command");
+        this.message = "Unknown command: " + command;
+    }
+
+    public String getCommand() {
+        return command;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public void setInputStream(InputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public void setOutputStream(OutputStream out) {
+        this.out = out;
+    }
+
+    @Override
+    public void setErrorStream(OutputStream err) {
+        this.err = err;
+    }
+
+    @Override
+    public void setExitCallback(ExitCallback callback) {
+        this.callback = callback;
+    }
+
+    @Override
+    public void run() {
+        String errorMessage = getMessage();
+        try {
+            try {
+                err.write(errorMessage.getBytes(StandardCharsets.UTF_8));
+                err.write('\n');
+            } finally {
+                err.flush();
+            }
+        } catch (IOException e) {
+            // ignored
+        }
+
+        if (callback != null) {
+            callback.onExit(1, errorMessage);
+        }
+    }
+
+    @Override
+    public void start(Environment env) throws IOException {
+        Thread thread = new Thread(this);
+        thread.setDaemon(true);
+        thread.start();
+    }
+
+    @Override
+    public void destroy() {
+        // ignored
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getCommand());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        return Objects.equals(this.getCommand(), ((UnknownCommand) obj).getCommand());
+    }
+
+    @Override
+    public String toString() {
+        return getMessage();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/af415e5f/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommandFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommandFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommandFactory.java
new file mode 100644
index 0000000..871ca60
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/shell/UnknownCommandFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.server.shell;
+
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UnknownCommandFactory implements CommandFactory {
+    public static final UnknownCommandFactory INSTANCE = new UnknownCommandFactory();
+
+    public UnknownCommandFactory() {
+        super();
+    }
+
+    @Override
+    public Command createCommand(String command) {
+        return new UnknownCommand(command);
+    }
+}