diff --git a/cli/pom.xml b/cli/pom.xml index af5934289..0eb777702 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -2,13 +2,12 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.cli jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH CLI The CRaSH command line interface module diff --git a/cli/src/main/java/org/crsh/cli/descriptor/OptionDescriptor.java b/cli/src/main/java/org/crsh/cli/descriptor/OptionDescriptor.java index 9368c1141..63136c45c 100644 --- a/cli/src/main/java/org/crsh/cli/descriptor/OptionDescriptor.java +++ b/cli/src/main/java/org/crsh/cli/descriptor/OptionDescriptor.java @@ -64,6 +64,7 @@ public class OptionDescriptor extends ParameterDescriptor { static { for (char c = 'a';c <= 'z';c++) { A.set(c); + A.set(c + 'A' - 'a'); } B.or(A); B.set('-'); diff --git a/cli/src/test/java/org/crsh/cli/impl/CommandInfoTestCase.java b/cli/src/test/java/org/crsh/cli/impl/CommandInfoTestCase.java index 9286c23a9..b181469a8 100644 --- a/cli/src/test/java/org/crsh/cli/impl/CommandInfoTestCase.java +++ b/cli/src/test/java/org/crsh/cli/impl/CommandInfoTestCase.java @@ -72,6 +72,17 @@ class A { assertEquals(Arrays.asList("i"),i.getNames()); } + public void testOptionWithUpperCase() throws IntrospectionException { + class A { + @Option(names = "I") + private int i; + } + CommandDescriptor ai = CommandFactory.DEFAULT.create(A.class); + assertEquals(1,ai.getOptions().size()); + OptionDescriptor i = ai.getOption("-I"); + assertEquals(Arrays.asList("I"),i.getNames()); + } + public void testArgument1() throws IntrospectionException { class A { @Argument() diff --git a/distrib/META-INF/MANIFEST.MF b/distrib/META-INF/MANIFEST.MF deleted file mode 100644 index 35289c8ad..000000000 --- a/distrib/META-INF/MANIFEST.MF +++ /dev/null @@ -1,4 +0,0 @@ -Manifest-Version: 1.0 -Archiver-Version: Plexus Archiver -Created-By: 20.6-b01-415 (Apple Inc.) - diff --git a/distrib/pom.xml b/distrib/pom.xml index 04f6c38f8..10b85b112 100644 --- a/distrib/pom.xml +++ b/distrib/pom.xml @@ -2,13 +2,12 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.distrib jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH distrib The CRaSH distribution @@ -198,76 +197,95 @@ + + make-gvm + package + + single + + + false + + src/main/assembly/gvm.xml + + + + - upload + gvm + + + org.codehaus.groovy + groovy-all + 2.0.7 + + + org.apache.ant + ant + 1.9.2 + + + org.codehaus.groovy.modules.http-builder + http-builder + 0.7.1 + + + org.apache.commons + commons-compress + 1.8.1 + + - com.googlecode.maven-gcu-plugin - maven-gcu-plugin + org.codehaus.gmaven + gmaven-plugin + 1.5 + + ${basedir}/src/bintrayUpload.groovy + install - upload + execute - - googlecode - true - crsh - - - ${project.build.directory}/crash-${project.version}-gatein.tar.gz - CRaSH GateIn web archive ${project.version} - - - - - - ${project.build.directory}/crash-${project.version}-mule-app.tar.gz - CRaSH Mule application ${project.version} - - - - - - ${project.build.directory}/crash-${project.version}-spring.tar.gz - CRaSH Spring web archive ${project.version} - - - - - - ${project.build.directory}/crash-${project.version}-war.tar.gz - CRaSH Standalone web archive ${project.version} - - - - - - ${project.build.directory}/crash-${project.version}-docs.tar.gz - CRaSH Documentation and sources bundle ${project.version} - - - - - - ${project.build.directory}/crash-${project.version}.tar.gz - CRaSH Standalone ${project.version} - - - - - - + + + org.codehaus.groovy + groovy-all + 2.0.7 + + + org.apache.ant + ant + 1.9.2 + + + org.codehaus.groovy.modules.http-builder + http-builder + 0.7.1 + + + org.apache.commons + commons-compress + 1.8.1 + + diff --git a/distrib/src/bintrayUpload.groovy b/distrib/src/bintrayUpload.groovy new file mode 100644 index 000000000..66264e2dc --- /dev/null +++ b/distrib/src/bintrayUpload.groovy @@ -0,0 +1,127 @@ +// This script uploads the gvm zip in bintray account +// it follows the gvm versionning scheme +// version with empty qualifiers or "crXYZ" will be uploaded +// SNAPSHOT version will be truncated to remove the SNAPSHOT as bintray does not accept them +// SNAPSHOT should be used for testing purpose + +import groovyx.net.http.HTTPBuilder +import org.apache.commons.compress.archivers.ArchiveOutputStream +import org.apache.commons.compress.archivers.ArchiveStreamFactory +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.PatternLayout; +import org.apache.log4j.Level; +import org.apache.log4j.Logger + +import static groovyx.net.http.ContentType.BINARY +import static groovyx.net.http.Method.* + +// Configure log4j +def console = new ConsoleAppender() +console.setLayout(new PatternLayout("%d [%p|%c|%C{1}] %m%n")); +console.setThreshold(Level.ALL); +console.activateOptions(); +Logger.getRootLogger().addAppender(console); +Logger.getRootLogger().setLevel(Level.INFO); +Logger.getLogger(HTTPBuilder.class).setLevel(Level.ALL) + +// Version check / change and so on +def pattern = ~/([0-9\.]+)(\-.+)?/ +def matcher = pattern.matcher(project.version); +if (!matcher.matches()) { + throw new Exception("Invalid version ${project.version} !") +} +def mmm = matcher.group(1); +def qualifier = matcher.group(2) +def version +if (qualifier != null && qualifier != "-SNAPSHOT") { + def qualifierPattern = ~/\-cr([0-9]+)(?:-SNAPSHOT)?/ + def qualifierMatcher = qualifierPattern.matcher(qualifier); + if (!qualifierMatcher.matches()) { + // Only candidate releases + return; + } + version = "${mmm}.RC${qualifierMatcher.group(1)}" +} else { + version = mmm; +} + +// Check we have the file to upload first +def file = new File(project.build.directory, "crash-${project.version}-gvm.zip") +if (!file.exists()) { + throw new Exception("${file.absolutePath} does not exists"); +} +if (!file.isFile()) { + throw new Exception("${file.absolutePath} is not a file"); +} + +// Transform file to prefix +ByteArrayOutputStream baos = new ByteArrayOutputStream((int)file.length()) +ZipArchiveInputStream zipIn = new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(file))); +ArchiveOutputStream zipOut = new ArchiveStreamFactory().createArchiveOutputStream("zip", baos); +byte[] buffer = new byte[512]; +while (true) { + ZipArchiveEntry entry = zipIn.getNextZipEntry(); + if (entry == null) { + break; + } else { + def length = (int)entry.size + entry.name = "crash-${version}/${entry.name}"; + if (entry.name.endsWith(".sh") || entry.name.endsWith(".bat")) { + entry.setUnixMode(0100755); + } + zipOut.putArchiveEntry(entry); + while (length > 0) { + def amount = zipIn.read(buffer, 0, Math.min(length, buffer.length)); + zipOut.write(buffer, 0, amount); + length -= amount; + } + zipOut.closeArchiveEntry(); + } +} +zipOut.close(); + +// Credentials +def bintrayUser = project.properties.bintrayUser; +def bintrayApiKey = project.properties.bintrayApiKey; +if (bintrayUser == null) { + throw new Exception("Property bintrayUser not found"); +} +if (bintrayApiKey == null) { + throw new Exception("Property bintrayApiKey not found"); +} + +// +def repoPath = "crashub/crash" +def packageName = "gvm" +def packagePath = "$repoPath/gvm" +def uploadUri = "/content/$packagePath/${version}/crash-${version}.zip"; + +// +def http = new HTTPBuilder("https://api.bintray.com"); +http.headers.Authorization = "Basic ${"$bintrayUser:$bintrayApiKey".toString().bytes.encodeBase64()}" + +println("Uploading ${file.absolutePath} to ${uploadUri}"); +def result = http.request(PUT) { + uri.path = uploadUri + requestContentType = BINARY + body = new ByteArrayInputStream(baos.toByteArray()); + response.success = { resp -> + return "Package ${packageName} uploaded."; + } + response.failure = { resp -> + def msg = "Cannot upload: ${resp.statusLine.statusCode} : ${resp.statusLine.reasonPhrase}"; + def entity = resp.entity; + if (entity != null) { + msg += "\n" + org.apache.http.util.EntityUtils.toString(entity); + } + return new Exception(msg); + } +} + +if (result instanceof Exception) { + throw result; +} else if (result != null) { + println(result) +} diff --git a/distrib/src/main/assembly/gvm.xml b/distrib/src/main/assembly/gvm.xml new file mode 100644 index 000000000..cdf655f48 --- /dev/null +++ b/distrib/src/main/assembly/gvm.xml @@ -0,0 +1,44 @@ + + + + gvm + + zip + + + / + + + src/main/assembly/distrib.xml + + + + + / + true + + org.crsh:crsh.shell.packaging:tar.gz + + + + + diff --git a/distrib/src/main/assembly/packaging.xml b/distrib/src/main/assembly/packaging.xml index d8c775618..e92b82b63 100644 --- a/distrib/src/main/assembly/packaging.xml +++ b/distrib/src/main/assembly/packaging.xml @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/distrib/src/main/izpack/crash/crash/crash.properties b/distrib/src/main/izpack/crash/crash/crash.properties deleted file mode 100644 index fd771bcf8..000000000 --- a/distrib/src/main/izpack/crash/crash/crash.properties +++ /dev/null @@ -1,19 +0,0 @@ -# VFS configuration -crash.vfs.refresh_period=1 - -# SSH configuration -crash.ssh.port=${crash.ssh.port} -#crash.ssh.keypath=/path/to/the/key/file - -# Telnet configuration -crash.telnet.port=${crash.telnet.port} - -# Authentication plugin configuration -crash.auth=${crash.auth} - -# Simple authentication plugin configuration -crash.auth.simple.username=${crash.auth.simple.username} -crash.auth.simple.password=${crash.auth.simple.password} - -# Jaas authentication plugin configuration -crash.auth.jaas.domain=${crash.auth.jaas.domain} diff --git a/distrib/src/main/izpack/crash/web.xml b/distrib/src/main/izpack/crash/web.xml deleted file mode 100644 index 0e8781897..000000000 --- a/distrib/src/main/izpack/crash/web.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - CRaSH - - - org.crsh.plugin.WebPluginLifeCycle - - - - doc/index.html - - - diff --git a/distrib/src/main/izpack/doc/index.html b/distrib/src/main/izpack/doc/index.html deleted file mode 100644 index dd5e3ee80..000000000 --- a/distrib/src/main/izpack/doc/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/distrib/src/main/izpack/install.xml b/distrib/src/main/izpack/install.xml deleted file mode 100644 index 2a2874d66..000000000 --- a/distrib/src/main/izpack/install.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - crash - @version@ - http://crsh.googlecode.com - - no - no - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The required CRaSH files. - - - - - - - Bundle the Groovy library with CRaSH. This is not required when the runtime provides this library already. - - - - - - Provides Telnet access to CRaSH. - - - - - - Provides SSH access to CRaSH. - - - - - - Bundle the Bouncy Castle libraries with CRaSH required for SSH support. This is not required when the runtime provides this library already. - - - - - - Bundle the CRaSH JCR extension. - - - - - - Bundle the CRaSH JCR eXo driver. - - - - - - Bundle the CRaSH JCR Jackrabbit driver. - - - - - - Bundle the CRaSH JCR documentation. - - - - - - - diff --git a/distrib/src/main/izpack/userInputSpec.xml b/distrib/src/main/izpack/userInputSpec.xml deleted file mode 100644 index 60b93be83..000000000 --- a/distrib/src/main/izpack/userInputSpec.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/cookbook/pom.xml b/doc/cookbook/pom.xml index 0a285723c..0a7ad5dba 100644 --- a/doc/cookbook/pom.xml +++ b/doc/cookbook/pom.xml @@ -22,13 +22,12 @@ crsh.doc org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.doc.cookbook jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH cookbook documentation The CRaSH cookbook documentation diff --git a/doc/cookbook/src/main/wikbook/en/en-US/FAQ.wiki b/doc/cookbook/src/main/wikbook/en/en-US/FAQ.wiki index cca038246..900eb3cdf 100644 --- a/doc/cookbook/src/main/wikbook/en/en-US/FAQ.wiki +++ b/doc/cookbook/src/main/wikbook/en/en-US/FAQ.wiki @@ -68,4 +68,16 @@ Remoting issue They will be in ##$CRASH_HOME/cmd/base## directory. You have to launch CRaSH once in standalone mode. +== I try to run CRaSH in Eclipse and it terminates immediately == + +CRaSH uses jline to handle keyboard input, and it's bypassing the Java API to read the key events. +Somewhere in the process there is a mismatch with the input handling in the Console view of Eclipse, and +CRaSH terminates without any reported error. You have to force jline to use a pure Java +handling of the keyboard events by adding the following JVM parameter in the Launch Configuration: + +{{screen}} +-Djline.terminal=jline.UnsupportedTerminal +{{/screen}} + +You will notice that the caret is not positioned correctly after submitting a command. diff --git a/doc/pom.xml b/doc/pom.xml index b443db494..c470942e9 100644 --- a/doc/pom.xml +++ b/doc/pom.xml @@ -3,13 +3,12 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.doc pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH doc parent diff --git a/doc/reference/pom.xml b/doc/reference/pom.xml index c6567b387..a5dd4a4eb 100644 --- a/doc/reference/pom.xml +++ b/doc/reference/pom.xml @@ -22,13 +22,12 @@ crsh.doc org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.doc.reference jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH reference documentation The CRaSH reference documentation diff --git a/doc/reference/src/main/wikbook/en/en-US/configuration.wiki b/doc/reference/src/main/wikbook/en/en-US/configuration.wiki index 8ff1153e7..d8e901d83 100644 --- a/doc/reference/src/main/wikbook/en/en-US/configuration.wiki +++ b/doc/reference/src/main/wikbook/en/en-US/configuration.wiki @@ -48,6 +48,17 @@ crash.ssh.port=2000 crash.telnet.port=5000 {{/code}} +== Changing SSH authentication and idle timeouts == + +Default authentication and idle timeout of the SSH server are set to 10 minutes (600'000 ms). Both timeouts can be configured in milliseconds with the //crash.ssh.auth-timeout// and //crash.ssh.idle-timeout// parameters in the //crash.properties// file + +{{code}} +# SSH configuration 5 minutes = 5 * 60 * 1000 = 300'000 +crash.ssh.auth-timeout=300000 +crash.ssh.idle-timeout=300000 +{{/code}} + + == Removing telnet or SSH access == * to remove the telnet access, remove the jar file in the //WEB-INF/lib/crsh.shell.telnet-$[crash.version].jar// . diff --git a/doc/reference/src/main/wikbook/en/en-US/running.wiki b/doc/reference/src/main/wikbook/en/en-US/running.wiki index 3d9b5fe29..0408bb2d4 100644 --- a/doc/reference/src/main/wikbook/en/en-US/running.wiki +++ b/doc/reference/src/main/wikbook/en/en-US/running.wiki @@ -185,6 +185,11 @@ Here is an example of embedding crash: 2000 + + 300000 + 300000 + + 5000 diff --git a/jcr/core/pom.xml b/jcr/core/pom.xml index 170d857d9..6e6f1d0a5 100644 --- a/jcr/core/pom.xml +++ b/jcr/core/pom.xml @@ -2,13 +2,13 @@ crsh.jcr org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.jcr.core jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH jcr core The CRaSH JCR module diff --git a/jcr/exo/pom.xml b/jcr/exo/pom.xml index 6321d949f..fb9eafc53 100644 --- a/jcr/exo/pom.xml +++ b/jcr/exo/pom.xml @@ -3,13 +3,13 @@ crsh.jcr org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.jcr.exo jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH jcr Exo Implementation The CRaSH JCR module for Exo Plateform diff --git a/jcr/jackrabbit/pom.xml b/jcr/jackrabbit/pom.xml index a1574bd11..b9680d897 100644 --- a/jcr/jackrabbit/pom.xml +++ b/jcr/jackrabbit/pom.xml @@ -3,13 +3,13 @@ crsh.jcr org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.jcr.jackrabbit jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH jcr Jackrabbit Implementation The CRaSH JCR module for Apache Jackrabbit diff --git a/jcr/pom.xml b/jcr/pom.xml index 5b83c2285..3c169d4dc 100644 --- a/jcr/pom.xml +++ b/jcr/pom.xml @@ -3,13 +3,13 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.jcr pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH JCR parent diff --git a/plugins/crowd/pom.xml b/plugins/crowd/pom.xml index 101de474d..55f7603b3 100644 --- a/plugins/crowd/pom.xml +++ b/plugins/crowd/pom.xml @@ -2,22 +2,20 @@ crsh.plugins org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.plugins.crowd jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH Plugin - Atlassian Crowd authentication and commands This plugin allows to delegate CRaSH authentication to an Atlassian Crowd directory and to add various commands to manage the server - ${project.groupId} + org.crsh crsh.shell.core - ${project.version} com.atlassian.crowd diff --git a/plugins/pom.xml b/plugins/pom.xml index 5ea3095ef..75892cdd9 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -3,13 +3,13 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.plugins pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH plugins parent diff --git a/pom.xml b/pom.xml index dfb7b3d05..e6efcdf4e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.crsh crsh.parent pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH parent The CRaSH is a shell for Java Content Repository that comes bundled as a war file to deploy in eXo Portal 2.5 or GateIn @@ -25,6 +25,7 @@ scm:git:git://github.com/crashub/crash.git scm:git:ssh://git@github.com/crashub/crash.git http://www.crashub.org + HEAD @@ -80,7 +81,7 @@ false - -Prelease,izpack,sign-artifacts + -Prelease,sign-artifacts false true @@ -103,12 +104,12 @@ org.crsh crsh.cli - 1.2.6 + 1.2.12-SNAPSHOT org.crsh crsh.cli - 1.2.6 + 1.2.12-SNAPSHOT sources @@ -116,25 +117,25 @@ org.crsh crsh.shell.core - 1.2.6 + 1.2.12-SNAPSHOT jar org.crsh crsh.shell.core - 1.2.6 + 1.2.12-SNAPSHOT test-jar org.crsh crsh.shell.core - 1.2.6 + 1.2.12-SNAPSHOT sources org.crsh crsh.shell.core - 1.2.6 + 1.2.12-SNAPSHOT standalone @@ -142,18 +143,18 @@ org.crsh crsh.shell.telnet - 1.2.6 + 1.2.12-SNAPSHOT org.crsh crsh.shell.telnet - 1.2.6 + 1.2.12-SNAPSHOT sources org.crsh crsh.shell.telnet - 1.2.6 + 1.2.12-SNAPSHOT standalone @@ -161,18 +162,18 @@ org.crsh crsh.shell.ssh - 1.2.6 + 1.2.12-SNAPSHOT org.crsh crsh.shell.ssh - 1.2.6 + 1.2.12-SNAPSHOT sources org.crsh crsh.shell.ssh - 1.2.6 + 1.2.12-SNAPSHOT standalone @@ -180,12 +181,12 @@ org.crsh crsh.shell.embed.spring - 1.2.6 + 1.2.12-SNAPSHOT org.crsh crsh.shell.embed.spring - 1.2.6 + 1.2.12-SNAPSHOT sources @@ -193,32 +194,32 @@ org.crsh crsh.shell.packaging - 1.2.6 + 1.2.12-SNAPSHOT org.crsh crsh.shell.packaging - 1.2.6 + 1.2.12-SNAPSHOT war org.crsh crsh.shell.packaging - 1.2.6 + 1.2.12-SNAPSHOT war spring org.crsh crsh.shell.packaging - 1.2.6 + 1.2.12-SNAPSHOT zip mule-app org.crsh crsh.shell.packaging - 1.2.6 + 1.2.12-SNAPSHOT tar.gz @@ -226,37 +227,37 @@ org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT jar org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT war org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT javadoc org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT test-jar org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT sources org.crsh crsh.jcr.core - 1.2.6 + 1.2.12-SNAPSHOT standalone @@ -264,19 +265,19 @@ org.crsh crsh.jcr.exo - 1.2.6 + 1.2.12-SNAPSHOT jar org.crsh crsh.jcr.exo - 1.2.6 + 1.2.12-SNAPSHOT war org.crsh crsh.jcr.exo - 1.2.6 + 1.2.12-SNAPSHOT sources @@ -284,19 +285,19 @@ org.crsh crsh.jcr.jackrabbit - 1.2.6 + 1.2.12-SNAPSHOT jar org.crsh crsh.jcr.jackrabbit - 1.2.6 + 1.2.12-SNAPSHOT war org.crsh crsh.jcr.jackrabbit - 1.2.6 + 1.2.12-SNAPSHOT sources @@ -304,13 +305,13 @@ org.crsh crsh.plugins.crowd - 1.2.6 + 1.2.12-SNAPSHOT jar org.crsh crsh.plugins.crowd - 1.2.6 + 1.2.12-SNAPSHOT sources @@ -318,20 +319,20 @@ org.crsh crsh.doc.reference - 1.2.6 + 1.2.12-SNAPSHOT pdf org.crsh crsh.doc.reference - 1.2.6 + 1.2.12-SNAPSHOT html zip org.crsh crsh.doc.reference - 1.2.6 + 1.2.12-SNAPSHOT javadoc @@ -339,13 +340,13 @@ org.crsh crsh.doc.cookbook - 1.2.6 + 1.2.12-SNAPSHOT pdf org.crsh crsh.doc.cookbook - 1.2.6 + 1.2.12-SNAPSHOT html zip @@ -554,7 +555,7 @@ org.apache.maven.plugins maven-release-plugin - 2.1 + 2.5 org.apache.maven.plugins diff --git a/shell/core/pom.xml b/shell/core/pom.xml index f5c51f141..f96d09dcf 100644 --- a/shell/core/pom.xml +++ b/shell/core/pom.xml @@ -2,13 +2,12 @@ crsh.shell org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.core jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH core The CRaSH core module diff --git a/shell/core/src/main/java/org/crsh/command/CRaSHCommand.java b/shell/core/src/main/java/org/crsh/command/CRaSHCommand.java index 0881c23d0..87469e98c 100644 --- a/shell/core/src/main/java/org/crsh/command/CRaSHCommand.java +++ b/shell/core/src/main/java/org/crsh/command/CRaSHCommand.java @@ -234,118 +234,88 @@ public final void execute(String s) throws ScriptException, IOException { final Class _producedType = producedType; // - if (consumedType == Void.class) { + return new CommandInvoker() { - return new CommandInvoker() { + /** . */ + PipeCommand real; - public Class getProducedType() { - return _producedType; - } + public Class getProducedType() { + return _producedType; + } - public Class getConsumedType() { - return _consumedType; - } + public Class getConsumedType() { + return _consumedType; + } - public void open(final CommandContext consumer) { - - // - pushContext(new InvocationContextImpl(consumer)); - CRaSHCommand.this.unmatched = match.getRest(); - final Resolver resolver = new Resolver() { - public T resolve(Class type) { - if (type.equals(InvocationContext.class)) { - return type.cast(peekContext()); - } else { - return null; - } - } - }; + public void open(final CommandContext consumer) { - // - Object o; - try { - o = invoker.invoke(resolver, CRaSHCommand.this); - } catch (org.crsh.cli.SyntaxException e) { - throw new org.crsh.command.SyntaxException(e.getMessage()); - } catch (InvocationException e) { - throw toScript(e.getCause()); - } - if (o != null) { - peekContext().getWriter().print(o); + // + final InvocationContextImpl invocationContext = new InvocationContextImpl(consumer); + final Resolver resolver = new Resolver() { + public T resolve(Class type) { + if (type.equals(InvocationContext.class)) { + return type.cast(invocationContext); + } else { + return null; + } } - } - public void provide(Object element) throws IOException { - // We just drop the elements - } - public void flush() throws IOException { - peekContext().flush(); - } - public void close() { - CRaSHCommand.this.unmatched = null; - popContext(); - } - }; - } else { - return new CommandInvoker() { + }; - /** . */ - PipeCommand real; + // Push context + pushContext(invocationContext); - public Class getProducedType() { - return _producedType; - } + // Set the unmatched part + CRaSHCommand.this.unmatched = match.getRest(); - public Class getConsumedType() { - return _consumedType; + // + Object ret; + try { + ret = invoker.invoke(resolver, CRaSHCommand.this); + } + catch (org.crsh.cli.SyntaxException e) { + throw new org.crsh.command.SyntaxException(e.getMessage()); + } catch (InvocationException e) { + throw toScript(e.getCause()); } - public void open(final CommandContext consumer) { - - // - final InvocationContextImpl invocationContext = new InvocationContextImpl(consumer); - - // - pushContext(invocationContext); - CRaSHCommand.this.unmatched = match.getRest(); - final Resolver resolver = new Resolver() { - public T resolve(Class type) { - if (type.equals(InvocationContext.class)) { - return type.cast(invocationContext); - } else { - return null; - } - } - }; - try { - real = (PipeCommand)invoker.invoke(resolver, CRaSHCommand.this); - } - catch (org.crsh.cli.SyntaxException e) { - throw new org.crsh.command.SyntaxException(e.getMessage()); - } catch (InvocationException e) { - throw toScript(e.getCause()); - } - - // + // It's a pipe command + if (ret instanceof PipeCommand) { + real = (PipeCommand)ret; real.doOpen(invocationContext); + } else { + if (ret != null) { + peekContext().getWriter().print(ret); + } } - - public void provide(Object element) throws IOException { + } + public void provide(Object element) throws IOException { + if (real != null) { real.provide(element); + } else { + // We just drop the elements } - - public void flush() throws IOException { + } + public void flush() throws IOException { + if (real != null) { real.flush(); + } else { + peekContext().flush(); } - - public void close() { + } + public void close() throws IOException { + if (real != null) { try { real.close(); } finally { popContext(); } + } else { + InvocationContext context = popContext(); + context.close(); } - }; - } + CRaSHCommand.this.unmatched = null; + } + }; } } diff --git a/shell/core/src/main/java/org/crsh/command/ClassDispatcher.java b/shell/core/src/main/java/org/crsh/command/ClassDispatcher.java index 66d47c30c..2dac341d5 100644 --- a/shell/core/src/main/java/org/crsh/command/ClassDispatcher.java +++ b/shell/core/src/main/java/org/crsh/command/ClassDispatcher.java @@ -24,6 +24,7 @@ import groovy.lang.MissingPropertyException; import org.codehaus.groovy.runtime.InvokerInvocationException; import org.crsh.io.Consumer; +import org.crsh.util.Safe; import java.io.IOException; import java.util.ArrayList; @@ -83,7 +84,6 @@ Object dispatch(String methodName, Object[] arguments) { // try { pipe.fire(); - pipe.close(); return null; } catch (ScriptException e) { @@ -94,6 +94,9 @@ Object dispatch(String methodName, Object[] arguments) { throw e; } } + finally { + Safe.close(pipe); + } } private PipeCommandProxy resolvePipe(String name, Object[] args, boolean piped) { diff --git a/shell/core/src/main/java/org/crsh/command/CommandContext.java b/shell/core/src/main/java/org/crsh/command/CommandContext.java index 72c0002b2..1ae71e5e5 100644 --- a/shell/core/src/main/java/org/crsh/command/CommandContext.java +++ b/shell/core/src/main/java/org/crsh/command/CommandContext.java @@ -20,12 +20,14 @@ import org.crsh.shell.InteractionContext; +import java.io.Closeable; + /** * The command context provides the services for invoking a command. * * @author Julien Viet */ -public interface CommandContext

extends InteractionContext

, RuntimeContext { +public interface CommandContext

extends InteractionContext

, RuntimeContext, Closeable { boolean isPiped(); diff --git a/shell/core/src/main/java/org/crsh/command/InnerInvocationContext.java b/shell/core/src/main/java/org/crsh/command/InnerInvocationContext.java index a2a93f153..8ae16d5da 100644 --- a/shell/core/src/main/java/org/crsh/command/InnerInvocationContext.java +++ b/shell/core/src/main/java/org/crsh/command/InnerInvocationContext.java @@ -123,6 +123,10 @@ public void flush() throws IOException { consumer.flush(); } + public void close() throws IOException { + // Nothing to do + } + public Map getSession() { return outter.getSession(); } diff --git a/shell/core/src/main/java/org/crsh/command/InvocationContextImpl.java b/shell/core/src/main/java/org/crsh/command/InvocationContextImpl.java index a37908b78..081cf02c1 100644 --- a/shell/core/src/main/java/org/crsh/command/InvocationContextImpl.java +++ b/shell/core/src/main/java/org/crsh/command/InvocationContextImpl.java @@ -121,6 +121,10 @@ public void flush() throws IOException { commandContext.flush(); } + public void close() throws IOException { + commandContext.close(); + } + public Map getSession() { return commandContext.getSession(); } diff --git a/shell/core/src/main/java/org/crsh/command/PipeCommandProxy.java b/shell/core/src/main/java/org/crsh/command/PipeCommandProxy.java index 10369c445..0d0daf9a3 100644 --- a/shell/core/src/main/java/org/crsh/command/PipeCommandProxy.java +++ b/shell/core/src/main/java/org/crsh/command/PipeCommandProxy.java @@ -70,7 +70,7 @@ public void flush() throws ScriptException, IOException { } } - public void close() throws ScriptException { + public void close() throws ScriptException, IOException { delegate.close(); if (next != null && next instanceof PipeCommand) { ((PipeCommand)next).close(); diff --git a/shell/core/src/main/java/org/crsh/io/Producer.java b/shell/core/src/main/java/org/crsh/io/Producer.java index 9a7b5d94c..add7dee1d 100644 --- a/shell/core/src/main/java/org/crsh/io/Producer.java +++ b/shell/core/src/main/java/org/crsh/io/Producer.java @@ -20,6 +20,7 @@ package org.crsh.io; import java.io.Closeable; +import java.io.IOException; /** * A producer that produces elements in a specific consumer. @@ -46,6 +47,6 @@ public interface Producer> extends Closeable { /** * Close the producer. */ - void close(); + void close() throws IOException; } diff --git a/shell/core/src/main/java/org/crsh/processor/jline/JLineProcessor.java b/shell/core/src/main/java/org/crsh/processor/jline/JLineProcessor.java index 098196c08..719154332 100644 --- a/shell/core/src/main/java/org/crsh/processor/jline/JLineProcessor.java +++ b/shell/core/src/main/java/org/crsh/processor/jline/JLineProcessor.java @@ -66,21 +66,35 @@ public void run() { loop(); } - private void loop() { + private String readLine() { + StringBuilder buffer = new StringBuilder(); + String prompt = getPrompt(); + writer.println(); + writer.flush(); while (true) { - String prompt = getPrompt(); - String line; try { - writer.println(); - writer.flush(); - if ((line = reader.readLine(prompt)) == null) { - break; + String chunk; + if ((chunk = reader.readLine(prompt)) == null) { + return null; + } + if (chunk.length() > 0 && chunk.charAt(chunk.length() - 1) == '\\') { + prompt = "> "; + buffer.append(chunk, 0, chunk.length() - 1); + } else { + buffer.append(chunk); + return buffer.toString(); } } catch (IOException e) { // What should we do other than that ? - break; + return null; } + } + } + + private void loop() { + while (true) { + String line = readLine(); // ShellProcess process = shell.createProcess(line); diff --git a/shell/core/src/main/java/org/crsh/processor/term/ProcessContext.java b/shell/core/src/main/java/org/crsh/processor/term/ProcessContext.java index ed9270493..95efa40c6 100644 --- a/shell/core/src/main/java/org/crsh/processor/term/ProcessContext.java +++ b/shell/core/src/main/java/org/crsh/processor/term/ProcessContext.java @@ -70,6 +70,7 @@ public String getProperty(String name) { public String readLine(String msg, boolean echo) { try { processor.term.provide(Text.create(msg)); + processor.term.flush(); } catch (IOException e) { return null; @@ -137,7 +138,7 @@ public void end(ShellResponse response) { switch (processor.status) { case PROCESSING: if (response instanceof ShellResponse.Close) { - runnable = processor.CLOSE; + runnable = processor.CLOSE_TASK; processor.status = Status.CLOSED; } else if (response instanceof ShellResponse.Cancelled) { runnable = Processor.NOOP; diff --git a/shell/core/src/main/java/org/crsh/processor/term/Processor.java b/shell/core/src/main/java/org/crsh/processor/term/Processor.java index bb8d12fd2..bd7df8f41 100644 --- a/shell/core/src/main/java/org/crsh/processor/term/Processor.java +++ b/shell/core/src/main/java/org/crsh/processor/term/Processor.java @@ -42,6 +42,9 @@ public final class Processor implements Runnable, Consumer { + /** . */ + private static final Text CONTINUE_PROMPT = Text.create("> "); + /** . */ static final Runnable NOOP = new Runnable() { public void run() { @@ -49,21 +52,21 @@ public void run() { }; /** . */ - final Runnable WRITE_PROMPT = new Runnable() { + final Runnable WRITE_PROMPT_TASK = new Runnable() { public void run() { writePromptFlush(); } }; /** . */ - final Runnable CLOSE = new Runnable() { + final Runnable CLOSE_TASK = new Runnable() { public void run() { close(); } }; /** . */ - private final Runnable READ_TERM = new Runnable() { + private final Runnable READ_TERM_TASK = new Runnable() { public void run() { readTerm(); } @@ -96,6 +99,9 @@ public void run() { /** . */ private final CloseableList listeners; + /** . */ + private final StringBuffer lineBuffer = new StringBuffer(); + public Processor(Term term, Shell shell) { this.term = term; this.shell = shell; @@ -154,7 +160,7 @@ boolean iterate() throws InterruptedException, IOException { } case PROCESSING: case CANCELLING: - runnable = READ_TERM; + runnable = READ_TERM_TASK; break; case CLOSED: return false; @@ -170,7 +176,6 @@ boolean iterate() throws InterruptedException, IOException { return true; } - // We assume this is called under lock synchronization ProcessContext peekProcess() { while (true) { synchronized (lock) { @@ -181,13 +186,27 @@ ProcessContext peekProcess() { complete(((TermEvent.Complete)event).getLine()); } else { String line = ((TermEvent.ReadLine)event).getLine().toString(); - if (line.length() > 0) { - term.addToHistory(line); + if (line.endsWith("\\")) { + lineBuffer.append(line, 0, line.length() - 1); + try { + term.provide(CONTINUE_PROMPT); + term.flush(); + } + catch (IOException e) { + e.printStackTrace(); + } + } else { + lineBuffer.append(line); + String command = lineBuffer.toString(); + lineBuffer.setLength(0); + if (command.length() > 0) { + term.addToHistory(command); + } + ShellProcess process = shell.createProcess(command); + current = new ProcessContext(this, process); + status = Status.PROCESSING; + return current; } - ShellProcess process = shell.createProcess(line); - current = new ProcessContext(this, process); - status = Status.PROCESSING; - return current; } } else { break; @@ -203,13 +222,14 @@ ProcessContext peekProcess() { /** . */ private final Object termLock = new Object(); - private boolean reading = false; + /** . */ + private boolean termReading = false; void readTerm() { // synchronized (termLock) { - if (reading) { + if (termReading) { try { termLock.wait(); return; @@ -218,7 +238,7 @@ void readTerm() { throw new AssertionError(e); } } else { - reading = true; + termReading = true; } } @@ -241,7 +261,7 @@ public void run() { }; } else if (status == Status.AVAILABLE) { - runnable = WRITE_PROMPT; + runnable = WRITE_PROMPT_TASK; } else { runnable = NOOP; } @@ -258,7 +278,7 @@ public void run() { } }; } else if (status != Status.CLOSED) { - runnable = CLOSE; + runnable = CLOSE_TASK; } else { runnable = NOOP; } @@ -279,7 +299,7 @@ public void run() { } finally { synchronized (termLock) { - reading = false; + termReading = false; termLock.notifyAll(); } } diff --git a/shell/core/src/main/java/org/crsh/shell/impl/command/CRaSHSession.java b/shell/core/src/main/java/org/crsh/shell/impl/command/CRaSHSession.java index dc4d73c14..0d6e8e208 100644 --- a/shell/core/src/main/java/org/crsh/shell/impl/command/CRaSHSession.java +++ b/shell/core/src/main/java/org/crsh/shell/impl/command/CRaSHSession.java @@ -148,7 +148,7 @@ private String eval(String name, String def) { ClassLoader previous = setCRaSHLoader(); try { GroovyShell shell = getGroovyShell(); - Object ret = shell.evaluate("return " + name + ";"); + Object ret = shell.getContext().getVariable(name); if (ret instanceof Closure) { log.log(Level.FINEST, "Invoking " + name + " closure"); Closure c = (Closure)ret; diff --git a/shell/core/src/main/java/org/crsh/shell/impl/command/Pipe.java b/shell/core/src/main/java/org/crsh/shell/impl/command/Pipe.java index 6d406f2ab..e7c3b1c7d 100644 --- a/shell/core/src/main/java/org/crsh/shell/impl/command/Pipe.java +++ b/shell/core/src/main/java/org/crsh/shell/impl/command/Pipe.java @@ -99,7 +99,7 @@ public void flush() throws IOException { command.flush(); } - public void close() throws ScriptException { + public void close() throws ScriptException, IOException { command.close(); } } diff --git a/shell/core/src/main/java/org/crsh/shell/impl/command/PipeFilter.java b/shell/core/src/main/java/org/crsh/shell/impl/command/PipeFilter.java index a0104a804..bfea7e015 100644 --- a/shell/core/src/main/java/org/crsh/shell/impl/command/PipeFilter.java +++ b/shell/core/src/main/java/org/crsh/shell/impl/command/PipeFilter.java @@ -107,7 +107,7 @@ public void open(CommandContext

consumer) { } public void close() { - Safe.close((Closeable)context); + Safe.close(context); } } @@ -158,8 +158,8 @@ public void flush() throws ScriptException, IOException { ca.flush(); } - public void close() throws ScriptException { - ((Pipe)context).close(); + public void close() throws ScriptException, IOException { + context.close(); } } @@ -194,8 +194,8 @@ public void flush() throws IOException { context.flush(); } - public void close() { - ((Pipe)context).close(); + public void close() throws IOException { + context.close(); } } } diff --git a/shell/core/src/main/java/org/crsh/shell/impl/command/PipeLine.java b/shell/core/src/main/java/org/crsh/shell/impl/command/PipeLine.java index 86da45dab..f4d4fd863 100644 --- a/shell/core/src/main/java/org/crsh/shell/impl/command/PipeLine.java +++ b/shell/core/src/main/java/org/crsh/shell/impl/command/PipeLine.java @@ -105,7 +105,7 @@ public void flush() throws IOException { current.flush(); } - public void close() { + public void close() throws IOException { current.close(); } } diff --git a/shell/core/src/main/java/org/crsh/text/formatter/ThreadRenderable.java b/shell/core/src/main/java/org/crsh/text/formatter/ThreadRenderable.java index ba5fe51e6..f0efb8ca7 100644 --- a/shell/core/src/main/java/org/crsh/text/formatter/ThreadRenderable.java +++ b/shell/core/src/main/java/org/crsh/text/formatter/ThreadRenderable.java @@ -79,6 +79,7 @@ public Renderer renderer(Iterator stream) { Thread.sleep(100); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } // Resample diff --git a/shell/core/src/main/java/org/crsh/text/ui/EvalElement.java b/shell/core/src/main/java/org/crsh/text/ui/EvalElement.java index 3ad1b0571..2c09e53f7 100644 --- a/shell/core/src/main/java/org/crsh/text/ui/EvalElement.java +++ b/shell/core/src/main/java/org/crsh/text/ui/EvalElement.java @@ -143,6 +143,10 @@ public void flush() throws IOException { renderers.add(i); } } + + public void close() throws IOException { + // Nothing to do, except maybe release resources (and also prevent to do any other operation) + } }; if (cmd instanceof CRaSHCommand) { diff --git a/shell/core/src/main/resources/crash/commands/base/jdbc.groovy b/shell/core/src/main/resources/crash/commands/base/jdbc.groovy index ec0788faf..ed11f4b0a 100644 --- a/shell/core/src/main/resources/crash/commands/base/jdbc.groovy +++ b/shell/core/src/main/resources/crash/commands/base/jdbc.groovy @@ -77,13 +77,19 @@ class jdbc implements Completer{ // We use this trick to work around the fact that the DriverManager#getConnection will not // use the thread context classloader because of the nasty DriverManager#getCallerClassLoader method - def getConnection = DriverManager.class.getDeclaredMethod("getConnection", String.class, Properties.class, ClassLoader.class) - if (!getConnection.accessible) - getConnection.accessible = true - - // try { - connection = getConnection.invoke(null, connectionString, props, Thread.currentThread().getContextClassLoader()); + try { + def getConnection = DriverManager.class.getDeclaredMethod("getConnection", String.class, Properties.class, ClassLoader.class); + getConnection.setAccessible(true); + connection = getConnection.invoke(null, connectionString, props, Thread.currentThread().getContextClassLoader()); + } + catch (NoSuchMethodException ignore) { + // JDK8 does not have this method instead it has the same method but with Class as last argument + // that we must invoke with null + def getConnection = DriverManager.class.getDeclaredMethod("getConnection", String.class, Properties.class, Class.class); + getConnection.setAccessible(true); + connection = getConnection.invoke(null, connectionString, props, null); + } } catch (InvocationTargetException ite) { throw ite.cause; diff --git a/shell/core/src/main/resources/crash/crash.properties b/shell/core/src/main/resources/crash/crash.properties index fa74253bd..cd18b6f23 100644 --- a/shell/core/src/main/resources/crash/crash.properties +++ b/shell/core/src/main/resources/crash/crash.properties @@ -5,6 +5,12 @@ crash.vfs.refresh_period=1 crash.ssh.port=2000 #crash.ssh.keypath=/path/to/the/key/file +# The idle-timeout for ssh sessions in milliseconds +crash.ssh.idle-timeout=600000 + +# The authentication timeout for ssh sessions in milliseconds +crash.ssh.auth-timeout=600000 + # Telnet configuration crash.telnet.port=5000 diff --git a/shell/core/src/test/java/org/crsh/processor/term/ProcessorTestCase.java b/shell/core/src/test/java/org/crsh/processor/term/ProcessorTestCase.java index f483888cc..1b0a3ca6e 100644 --- a/shell/core/src/test/java/org/crsh/processor/term/ProcessorTestCase.java +++ b/shell/core/src/test/java/org/crsh/processor/term/ProcessorTestCase.java @@ -28,6 +28,7 @@ import org.crsh.cli.impl.Delimiter; import org.crsh.cli.spi.Completion; import org.crsh.shell.Shell; +import org.crsh.shell.ShellProcess; import org.crsh.shell.ShellProcessContext; import org.crsh.shell.ShellResponse; import org.crsh.term.console.ConsoleTerm; @@ -35,6 +36,8 @@ import org.crsh.text.CLS; import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -251,6 +254,37 @@ public CompletionMatch complete(String prefix) { controller.connector.assertChars("ba").assertFlush(); } + public void testMultiLine() throws Exception { + final LinkedList requests = new LinkedList(); + final CountDownLatch latch = new CountDownLatch(1); + Controller controller = create(new BaseShell(BaseProcessFactory.ECHO) { + @Override + public ShellProcess createProcess(String request) { + return new BaseProcess(request) { + @Override + protected ShellResponse execute(String request) { + requests.add(request); + latch.countDown(); + return super.execute(request); + } + }; + } + }); + controller.assertStart(); + + // + controller.connector.append("a\\\r\n"); + controller.connector.assertChars("a").assertFlush(); + controller.connector.assertChars("\\").assertFlush(); + controller.connector.assertCRLF().assertFlush().assertChars("> ").assertFlush(); + assertEquals(Collections.emptyList(), requests); + controller.connector.append("b\r\n"); + controller.connector.assertChars("b").assertFlush(); + controller.connector.assertCRLF().assertFlush(); + latch.await(5, TimeUnit.SECONDS); + assertEquals(Collections.singletonList("ab"), requests); + } + public void testCLS() throws Exception { Controller controller = create(new BaseShell(new BaseProcessFactory() { @Override diff --git a/shell/core/src/test/java/org/crsh/shell/Commands.java b/shell/core/src/test/java/org/crsh/shell/Commands.java index bd3e808b0..411eab129 100644 --- a/shell/core/src/test/java/org/crsh/shell/Commands.java +++ b/shell/core/src/test/java/org/crsh/shell/Commands.java @@ -169,6 +169,22 @@ public void provide(String element) throws ScriptException, IOException { } } + public static class IsClosed extends CRaSHCommand { + + /** . */ + public static final AtomicInteger closed = new AtomicInteger(); + + @Command + public org.crsh.command.PipeCommand main() { + return new PipeCommand() { + @Override + public void close() throws ScriptException { + closed.incrementAndGet(); + } + }; + } + } + public static class IsPiped extends CRaSHCommand { @Command public org.crsh.command.PipeCommand main() { diff --git a/shell/core/src/test/java/org/crsh/shell/HelpTestCase.java b/shell/core/src/test/java/org/crsh/shell/HelpTestCase.java new file mode 100644 index 000000000..c9e63775d --- /dev/null +++ b/shell/core/src/test/java/org/crsh/shell/HelpTestCase.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 eXo Platform SAS. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.crsh.shell; + +/** @author Julien Viet */ +public class HelpTestCase extends AbstractCommandTestCase { + + public void testPiped() { + lifeCycle.bind("piped", Commands.IsPiped.class); + String resp = assertOk("piped -h"); + assertTrue(resp.contains("usage")); + } +} diff --git a/shell/core/src/test/java/org/crsh/shell/PipeTestCase.java b/shell/core/src/test/java/org/crsh/shell/PipeTestCase.java index 6cd0a2d42..c9d7d4b8d 100644 --- a/shell/core/src/test/java/org/crsh/shell/PipeTestCase.java +++ b/shell/core/src/test/java/org/crsh/shell/PipeTestCase.java @@ -19,7 +19,6 @@ package org.crsh.shell; -import org.codehaus.groovy.tools.shell.Command; import org.crsh.command.ScriptException; import org.crsh.text.ChunkBuffer; @@ -28,6 +27,20 @@ public class PipeTestCase extends AbstractCommandTestCase { + public void testClose() { + lifeCycle.bind("closed", Commands.IsClosed.class); + + // + Commands.IsClosed.closed.set(0); + assertEquals("", assertOk("closed")); + assertEquals(1, Commands.IsClosed.closed.get()); + + // + Commands.IsClosed.closed.set(0); + assertEquals("", assertOk("echo abc | closed")); + assertEquals(1, Commands.IsClosed.closed.get()); + } + public void testIsPiped() { lifeCycle.bind("piped", Commands.IsPiped.class); lifeCycle.bind("produce_command", Commands.ProduceString.class); diff --git a/shell/core/src/test/java/org/crsh/shell/TestInvocationContext.java b/shell/core/src/test/java/org/crsh/shell/TestInvocationContext.java index c51f8eff4..779bee2d9 100644 --- a/shell/core/src/test/java/org/crsh/shell/TestInvocationContext.java +++ b/shell/core/src/test/java/org/crsh/shell/TestInvocationContext.java @@ -109,6 +109,10 @@ public void flush() throws IOException { producer.flush(); } + public void close() throws IOException { + // + } + public CommandInvoker resolve(String s) throws ScriptException, IOException { throw new UnsupportedOperationException(); } diff --git a/shell/embed/pom.xml b/shell/embed/pom.xml index 646f5eaf4..c776fdcfd 100644 --- a/shell/embed/pom.xml +++ b/shell/embed/pom.xml @@ -3,13 +3,12 @@ crsh.shell org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.embed pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH shell embed parent diff --git a/shell/embed/spring/pom.xml b/shell/embed/spring/pom.xml index f980fbcfe..bd181711d 100644 --- a/shell/embed/spring/pom.xml +++ b/shell/embed/spring/pom.xml @@ -2,13 +2,12 @@ crsh.shell.embed org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.embed.spring jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH Spring The CRaSH Spring integration module @@ -30,14 +29,6 @@ org.springframework spring-core - org.springframework @@ -51,26 +42,12 @@ org.springframework spring-beans - org.crsh crsh.shell.telnet test - org.crsh crsh.shell.core diff --git a/shell/packaging/pom.xml b/shell/packaging/pom.xml index 67d2e6153..cc05f46df 100644 --- a/shell/packaging/pom.xml +++ b/shell/packaging/pom.xml @@ -2,13 +2,12 @@ crsh.shell org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.packaging jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH shell packaging The CRaSH shell packaging module diff --git a/shell/packaging/src/main/packaging/bin/crash.bat b/shell/packaging/src/main/packaging/bin/crash.bat index 8658727f2..a8930763d 100644 --- a/shell/packaging/src/main/packaging/bin/crash.bat +++ b/shell/packaging/src/main/packaging/bin/crash.bat @@ -40,7 +40,7 @@ if not exist "%CRASH_HOME%\tmp" mkdir %CRASH_HOME%\tmp REM start the application with all parameters. Add tools.jar to the bootclasspath, otherwise it cannot be found REM echo "java -Xbootclasspath/a:%TOOLS_JAR% -classpath %CLASSPATH% %CRASH_DEBUG_OPTS% -Djava.util.logging.config.file=%CRASH_HOME%\conf\logging.properties org.crsh.cli.impl.bootstrap.Main --conf %CRASH_HOME%\conf --cmd %CRASH_HOME%\cmd %CMD_LINE_ARGS%" -java -Xbootclasspath/a:"%TOOLS_JAR%" -classpath "%CLASSPATH%" %CRASH_DEBUG_OPTS% -Djava.util.logging.config.file="%CRASH_HOME%\conf\logging.properties" org.crsh.cli.impl.bootstrap.Main -jar "%CRASH_HOME%\bin\%JARNAME%" --conf "%CRASH_HOME%\conf" --cmd "%CRASH_HOME%\cmd" %CMD_LINE_ARGS% +java -Xbootclasspath/a:"%TOOLS_JAR%" -classpath "%CLASSPATH%" %CRASH_DEBUG_OPTS% -Djava.util.logging.config.file="%CRASH_HOME%\conf\logging.properties" org.crsh.cli.impl.bootstrap.Main --conf "%CRASH_HOME%\conf" --cmd "%CRASH_HOME%\cmd" %CMD_LINE_ARGS% set ERROR_CODE=%ERRORLEVEL% endlocal & set ERROR_CODE=%ERROR_CODE% diff --git a/shell/packaging/src/main/webapp/WEB-INF/crash/crash.properties b/shell/packaging/src/main/webapp/WEB-INF/crash/crash.properties index 7e0705d1a..324bcc29e 100644 --- a/shell/packaging/src/main/webapp/WEB-INF/crash/crash.properties +++ b/shell/packaging/src/main/webapp/WEB-INF/crash/crash.properties @@ -5,6 +5,12 @@ crash.vfs.refresh_period=1 crash.ssh.port=2000 #crash.ssh.keypath=/path/to/the/key/file +# The idle-timeout for ssh sessions in milliseconds +crash.ssh.idle-timeout=600000 + +# The authentication timeout for ssh sessions in milliseconds +crash.ssh.auth-timeout=600000 + # Telnet configuration crash.telnet.port=5000 diff --git a/shell/pom.xml b/shell/pom.xml index fa7244234..c9e53ef71 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -3,13 +3,13 @@ crsh.parent org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 org.crsh crsh.shell pom - 1.2.6 + 1.2.12-SNAPSHOT CRaSH shell parent diff --git a/shell/ssh/pom.xml b/shell/ssh/pom.xml index d473ba922..9703f2755 100644 --- a/shell/ssh/pom.xml +++ b/shell/ssh/pom.xml @@ -2,13 +2,12 @@ crsh.shell org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.ssh jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH ssh The CRaSH ssh module @@ -22,7 +21,6 @@ - org.apache.sshd sshd-core diff --git a/shell/ssh/src/main/java/org/crsh/ssh/SSHPlugin.java b/shell/ssh/src/main/java/org/crsh/ssh/SSHPlugin.java index 0ff8c96a0..b6784af1d 100644 --- a/shell/ssh/src/main/java/org/crsh/ssh/SSHPlugin.java +++ b/shell/ssh/src/main/java/org/crsh/ssh/SSHPlugin.java @@ -43,7 +43,16 @@ public class SSHPlugin extends CRaSHPlugin { /** The SSH server key path. */ public static final PropertyDescriptor SSH_SERVER_KEYPATH = PropertyDescriptor.create("ssh.keypath", (String)null, "The path to the key file"); - /** . */ + /** The SSH server idle timeout. */ + private static final int SSH_SERVER_IDLE_DEFAULT_TIMEOUT = 10 * 60 * 1000; + public static final PropertyDescriptor SSH_SERVER_IDLE_TIMEOUT = PropertyDescriptor.create("ssh.idle-timeout", SSH_SERVER_IDLE_DEFAULT_TIMEOUT, "The idle-timeout for ssh sessions"); + + /** The SSH server authentication timeout. */ + private static final int SSH_SERVER_AUTH_DEFAULT_TIMEOUT = 10 * 60 * 1000; + public static final PropertyDescriptor SSH_SERVER_AUTH_TIMEOUT = PropertyDescriptor.create("ssh.auth-timeout", SSH_SERVER_AUTH_DEFAULT_TIMEOUT, "The authentication timeout for ssh sessions"); + + + /** . */ private SSHLifeCycle lifeCycle; @Override @@ -53,7 +62,7 @@ public SSHPlugin getImplementation() { @Override protected Iterable> createConfigurationCapabilities() { - return Arrays.>asList(SSH_PORT, SSH_SERVER_KEYPATH, AuthenticationPlugin.AUTH); + return Arrays.>asList(SSH_PORT, SSH_SERVER_KEYPATH, SSH_SERVER_IDLE_TIMEOUT, SSH_SERVER_AUTH_TIMEOUT, AuthenticationPlugin.AUTH); } @Override @@ -67,6 +76,15 @@ public void init() { return; } + Integer idleTimeout = getContext().getProperty(SSH_SERVER_IDLE_TIMEOUT); + if (idleTimeout == null) { + idleTimeout = SSH_SERVER_IDLE_DEFAULT_TIMEOUT; + } + Integer authTimeout = getContext().getProperty(SSH_SERVER_AUTH_TIMEOUT); + if (authTimeout == null) { + authTimeout = SSH_SERVER_AUTH_DEFAULT_TIMEOUT; + } + // Resource serverKey = null; @@ -97,8 +115,11 @@ public void init() { if (f.exists() && f.isFile()) { try { serverKeyURL = f.toURI().toURL(); + serverKey = new Resource(serverKeyURL); } catch (MalformedURLException e) { log.log(Level.FINE, "Ignoring invalid server key " + serverKeyPath, e); + } catch (IOException e) { + log.log(Level.FINE, "Could not load ssh key from " + serverKeyURL, e); } } else { log.log(Level.FINE, "Ignoring invalid server key path " + serverKeyPath); @@ -128,6 +149,8 @@ public void init() { SSHLifeCycle lifeCycle = new SSHLifeCycle(getContext(), authPlugin); lifeCycle.setPort(port); lifeCycle.setKey(serverKey); + lifeCycle.setAuthTimeout(authTimeout); + lifeCycle.setIdleTimeout(idleTimeout); lifeCycle.init(); // diff --git a/shell/ssh/src/main/java/org/crsh/ssh/term/SSHLifeCycle.java b/shell/ssh/src/main/java/org/crsh/ssh/term/SSHLifeCycle.java index 453993bee..08bdae156 100644 --- a/shell/ssh/src/main/java/org/crsh/ssh/term/SSHLifeCycle.java +++ b/shell/ssh/src/main/java/org/crsh/ssh/term/SSHLifeCycle.java @@ -22,9 +22,11 @@ import org.apache.sshd.common.Session; import org.apache.sshd.server.PasswordAuthenticator; import org.apache.sshd.server.PublickeyAuthenticator; +import org.apache.sshd.server.ServerFactoryManager; import org.apache.sshd.server.session.ServerSession; import org.crsh.plugin.PluginContext; import org.crsh.auth.AuthenticationPlugin; +import org.crsh.ssh.SSHPlugin; import org.crsh.ssh.term.scp.SCPCommandFactory; import org.crsh.term.TermLifeCycle; import org.crsh.term.spi.TermIOHandler; @@ -54,7 +56,15 @@ public class SSHLifeCycle extends TermLifeCycle { /** . */ private int port; + + /** . */ + private int idleTimeout; + /** . */ + private int authTimeout; + + + /** . */ private Resource key; /** . */ @@ -78,7 +88,23 @@ public void setPort(int port) { this.port = port; } - /** + public int getIdleTimeout() { + return idleTimeout; + } + + public void setIdleTimeout(int idleTimeout) { + this.idleTimeout = idleTimeout; + } + + public int getAuthTimeout() { + return authTimeout; + } + + public void setAuthTimeout(int authTimeout) { + this.authTimeout = authTimeout; + } + + /** * Returns the local part after the ssh server has been succesfully bound or null. This is useful when * the port is chosen at random by the system. * @@ -106,6 +132,15 @@ protected void doInit() { // SshServer server = SshServer.setUpDefaultServer(); server.setPort(port); + + if (this.idleTimeout > 0) { + server.getProperties().put(ServerFactoryManager.IDLE_TIMEOUT, String.valueOf(this.idleTimeout)); + } + if (this.authTimeout > 0) { + server.getProperties().put(ServerFactoryManager.AUTH_TIMEOUT, String.valueOf(this.authTimeout)); + } + + server.setShellFactory(new CRaSHCommandFactory(handler)); server.setCommandFactory(new SCPCommandFactory(getContext())); server.setKeyPairProvider(new URLKeyPairProvider(key)); diff --git a/shell/ssh/src/main/java/org/crsh/ssh/term/inline/SSHInlineCommand.java b/shell/ssh/src/main/java/org/crsh/ssh/term/inline/SSHInlineCommand.java index 3f395acf6..428e9c769 100644 --- a/shell/ssh/src/main/java/org/crsh/ssh/term/inline/SSHInlineCommand.java +++ b/shell/ssh/src/main/java/org/crsh/ssh/term/inline/SSHInlineCommand.java @@ -91,6 +91,8 @@ public String getName() { } else { String errorMsg; + // Set the exit status to Error + exitStatus = ERROR; if (response != null) { errorMsg = "Error during command execution : " + response.getMessage(); } diff --git a/shell/ssh/src/test/java/org/crsh/ssh/SSHTestCase.java b/shell/ssh/src/test/java/org/crsh/ssh/SSHTestCase.java index 9dd1a2f78..e1f25e4b2 100644 --- a/shell/ssh/src/test/java/org/crsh/ssh/SSHTestCase.java +++ b/shell/ssh/src/test/java/org/crsh/ssh/SSHTestCase.java @@ -59,6 +59,8 @@ public void setUp() throws Exception { // TestPluginLifeCycle lifeCycle = new TestPluginLifeCycle(new SSHPlugin(), handler, auth); lifeCycle.setProperty(SSHPlugin.SSH_PORT, port); + lifeCycle.setProperty(SSHPlugin.SSH_SERVER_IDLE_TIMEOUT, 10 * 60 * 1000); + lifeCycle.setProperty(SSHPlugin.SSH_SERVER_AUTH_TIMEOUT, 10 * 60 * 1000); lifeCycle.setProperty(AuthenticationPlugin.AUTH, auth.getName()); lifeCycle.setProperty(SimpleAuthenticationPlugin.SIMPLE_USERNAME, "root"); lifeCycle.setProperty(SimpleAuthenticationPlugin.SIMPLE_PASSWORD, ""); diff --git a/shell/telnet/pom.xml b/shell/telnet/pom.xml index 92499fd54..3c7d30d01 100644 --- a/shell/telnet/pom.xml +++ b/shell/telnet/pom.xml @@ -2,13 +2,12 @@ crsh.shell org.crsh - 1.2.6 + 1.2.12-SNAPSHOT 4.0.0 - org.crsh crsh.shell.telnet jar - 1.2.6 + 1.2.12-SNAPSHOT CRaSH telnet The CRaSH telner module