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);
+ }
+}