├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── LICENSE.TXT ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs-src ├── WebServer-1105-README.md └── WebServer-1105.zip ├── release └── upload_android_artifacts.sh ├── settings.gradle.example ├── src ├── instrumentTest │ ├── assets │ │ └── test.properties │ └── java │ │ └── com │ │ └── couchbase │ │ └── lite │ │ └── testapp │ │ └── listener │ │ └── tests │ │ └── LiteListenerTestCase.java └── main │ ├── java │ └── com │ │ └── couchbase │ │ └── lite │ │ └── listener │ │ ├── Credentials.java │ │ ├── LiteListener.java │ │ ├── LiteServer.java │ │ └── LiteServlet.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ └── values │ ├── strings.xml │ └── styles.xml └── vendor └── tjws ├── LICENSE.txt ├── README.md └── src └── java └── Acme ├── Resource └── mime.properties ├── Serve ├── CgiServlet.java ├── FileServlet.java ├── Main.java ├── SSLAcceptor.java ├── SelectorAcceptor.java ├── Serve.java ├── SimpleAcceptor.java ├── ThrottleItem.java ├── ThrottledOutputStream.java └── WarDeployer.java ├── Utils.java └── WildcardDictionary.java /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | *.iml 3 | /.DS_Store 4 | local.properties 5 | \#* 6 | *~ 7 | .#* 8 | \#*\# 9 | *.class 10 | .idea 11 | .gradle 12 | settings.gradle 13 | 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libraries/couchbase-lite-java-core"] 2 | path = libraries/couchbase-lite-java-core 3 | url = https://github.com/couchbase/couchbase-lite-java-core.git 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We hate bugs, but we love bug reports! And we're grateful to our developers who exercise Couchbase Lite and the Couchbase Sync Gateway in new and unexpected ways and help us work out the kinks. 2 | 3 | We also want to hear about your ideas for new features and improvements. You can file those in the issue trackers too. 4 | 5 | And while we welcome questions, **we prefer to answer questions on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/mobile-couchbase)** rather than in Github issues. 6 | 7 | # 1. Is This A Duplicate? 8 | 9 | It's great if you can scan the open issues to see if your problem/idea has been reported already. If so, feel free to add any new details or just a note that you hit this too. But if you're in a hurry, go ahead and skip this step -- we'd rather get duplicate reports than miss an issue! 10 | 11 | # 2. Describe The Bug 12 | 13 | ## Version 14 | 15 | Please indicate **what version of the software** you're using. If you compiled it yourself from source, it helps if you give the Git commit ID, or at least the branch name and date ("I last pulled from master on 6/30.") 16 | 17 | If the bug involves replication, also indicate what software and version is on the other end of the line, i.e. "Couchbase Lite Android 1.0" or "Sync Gateway 1.0" or "Sync Gateway commit f3d3229c" or "CouchDB 1.6". 18 | 19 | ## Include Steps To Reproduce 20 | 21 | The most **important information** to provide with a bug report is a clear set of steps to reproduce the problem. Include as much information as possible that you think may be related to the bug. An example would be: 22 | 23 | * Install app on ios6 device 24 | * Login with facebook 25 | * Turn off wifi 26 | * Add a new document 27 | * Turn on wifi 28 | * Saw no new documents on Sync Gateway (expected: there should have been some documents) 29 | 30 | ## Include Actual vs. Expected 31 | 32 | As mentioned above, the last thing in your steps to reproduce is the "actual vs expected" behavior. The reason this is important is because you may have a gross misunderstanding on what is supposed to happen. If you give a clear description of what actually happened as well as what you were expecting to happen, it will make the bug a lot easier to figure out. 33 | 34 | ## General Formatting 35 | 36 | Please **format source code or program output (including logs or backtraces) as code**. This makes it easier to read and prevents Github from interpreting any of it as Markdown formatting or bug numbers. To do this, put a line of just three back-quotes ("```") before and after it. (For inline code snippets, just put a single back-quote before and after.) 37 | 38 | **If you need to post a block of code/output that is longer than 1/2 a page, please don't paste it into the bug report** -- it's annoying to scroll past. Instead, create a [gist](https://gist.github.com) (or something similar) and just post a link to it. 39 | 40 | ## Crashes / Exceptions 41 | 42 | If the bug causes a crash or an uncaught exception, include a crash log or backtrace. **Please don't add this as a screenshot of the IDE** if you have any alternative. (In Xcode, use the `bt` command in the debugger console to dump a backtrace that you can copy.) 43 | 44 | 45 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## couchbase-lite-java-listener 2 | 3 | This provides a webserver wrapper around Couchbase-Lite so that it can be called via HTTP REST calls. 4 | 5 | ## How to add to your project 6 | 7 | This can be added either via a source dependency or a maven artifact dependency. 8 | 9 | See the [couchbase-lite-android-liteserv](https://github.com/couchbaselabs/couchbase-lite-android-liteserv), which provides an example with both dependency styles. 10 | 11 | ## TJWS dependency 12 | 13 | Couchbase-lite android depends on the [Tiny Java Web Server and Servlet Container](http://tjws.sourceforge.net/). See libs-src/Webserver-194-README.md for more details. 14 | 15 | ## Ektorp Gotchas 16 | 17 | If you are connecting to CBLiteListener from a client that uses Ektorp, you may run into issues: issue #5, issue #7, or issue #8. 18 | 19 | The workaround is to call: 20 | 21 | ``` 22 | StdHttpClient.Builder builder = new StdHttpClient.Builder().host(hostName).port(port).useExpectContinue(false); 23 | return builder.build(); 24 | ``` 25 | 26 | (the key point being to use `useExpectContinue(false)`) 27 | 28 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'java' 3 | apply plugin: 'maven' 4 | apply plugin: 'eclipse' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_1_6 7 | targetCompatibility = JavaVersion.VERSION_1_6 8 | 9 | repositories { 10 | mavenLocal() 11 | maven { url 'http://files.couchbase.com/maven2/' } 12 | mavenCentral() 13 | } 14 | 15 | sourceSets { 16 | main { 17 | java.srcDirs = [ 18 | 'src/main/java', 19 | 'vendor/tjws/src/java' 20 | ] 21 | } 22 | } 23 | 24 | def buildListenerWithArtifacts = System.getProperty("buildListenerWithArtifacts") 25 | 26 | dependencies { 27 | 28 | testCompile group: 'junit', name: 'junit', version: '4.11' 29 | 30 | compile 'com.couchbase.cblite:servlet:2-3' 31 | 32 | compile buildListenerWithArtifacts == null ? 33 | project(':libraries:couchbase-lite-java-core') : 34 | 'com.couchbase.lite:couchbase-lite-java-core:' + System.getenv("MAVEN_UPLOAD_VERSION") 35 | } 36 | 37 | task createMavenDirectory(type: Exec) { 38 | 39 | ext { 40 | uploadUser = System.getenv("MAVEN_UPLOAD_USERNAME") + ":" + System.getenv("MAVEN_UPLOAD_PASSWORD") 41 | mkcolPath = System.getenv("MAVEN_UPLOAD_REPO_URL") + "com/couchbase/lite/couchbase-lite-java-listener/" + System.getenv("MAVEN_UPLOAD_VERSION") + "/" 42 | } 43 | commandLine "curl", "--user", uploadUser, "-X", "MKCOL", mkcolPath 44 | } 45 | 46 | // this hack is only needed for apache mod_dav based Maven repo's like file.couchbase.com. otherwise, skip it 47 | createMavenDirectory.onlyIf { System.getenv("MAVEN_UPLOAD_REPO_URL").contains("files") } 48 | 49 | task uploadArchivesWrapper(dependsOn: createMavenDirectory) << { 50 | uploadArchives.execute() 51 | } 52 | 53 | def mavenPath() { 54 | System.getenv("MAVEN_BUILD_LOCAL") == "true" ? 55 | 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath : 56 | System.getenv("MAVEN_UPLOAD_REPO_URL") 57 | } 58 | 59 | uploadArchives { 60 | repositories { 61 | mavenDeployer { 62 | repository(url: mavenPath()) { 63 | authentication(userName: System.getenv("MAVEN_UPLOAD_USERNAME"), password: System.getenv("MAVEN_UPLOAD_PASSWORD")) 64 | } 65 | pom.version = System.getenv("MAVEN_UPLOAD_VERSION") != null ? System.getenv("MAVEN_UPLOAD_VERSION") : '' 66 | pom.groupId = 'com.couchbase.lite' 67 | pom.artifactId = 'couchbase-lite-java-listener' 68 | pom.project { 69 | licenses { 70 | license { 71 | name 'Couchbase Community Edition License Agreement' 72 | url 'http://www.couchbase.com/agreement/community' 73 | distribution 'repo' 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | task sourcesJar(type: Jar) { 82 | classifier = 'sources' 83 | from sourceSets.main.java.srcDirs 84 | } 85 | 86 | artifacts { 87 | archives sourcesJar 88 | } 89 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/couchbase/couchbase-lite-java-listener/b803ce2a88455dc515aeccbd11d369e434cbcc26/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Feb 06 16:23:51 PST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://downloads.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /libs-src/WebServer-1105-README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Couchbase-lite android depends on the [Tiny Java Web Server and Servlet Container](http://tjws.sourceforge.net/). 4 | 5 | We are using release 1.105. The source distribution has been checked into this repo in case it ever disappears from the internet. 6 | 7 | TJWS is being actively maintained and we will check for updates. However if there is an update that you could use then open an issue. 8 | 9 | 10 | -------------------------------------------------------------------------------- /libs-src/WebServer-1105.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/couchbase/couchbase-lite-java-listener/b803ce2a88455dc515aeccbd11d369e434cbcc26/libs-src/WebServer-1105.zip -------------------------------------------------------------------------------- /release/upload_android_artifacts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | : ${MAVEN_UPLOAD_VERSION:?"Need to set 'MAVEN_UPLOAD_VERSION' non-empty"} 3 | : ${MAVEN_UPLOAD_USERNAME:?"Need to set MAVEN_UPLOAD_USERNAME non-empty"} 4 | : ${MAVEN_UPLOAD_PASSWORD:?"Need to set MAVEN_UPLOAD_PASSWORD non-empty"} 5 | : ${MAVEN_UPLOAD_REPO_URL:?"Need to set MAVEN_UPLOAD_REPO_URL non-empty"} 6 | 7 | #at first build all projects 8 | ./gradlew :libraries:couchbase-lite-java-core:build && ./gradlew :build -DbuildListenerWithArtifacts && 9 | #then upload artifacts 10 | ./gradlew :uploadArchivesWrapper -DbuildListenerWithArtifacts 11 | -------------------------------------------------------------------------------- /settings.gradle.example: -------------------------------------------------------------------------------- 1 | include ':couchbase-lite-java-listener', ':libraries:couchbase-lite-java-core' -------------------------------------------------------------------------------- /src/instrumentTest/assets/test.properties: -------------------------------------------------------------------------------- 1 | # if you want to change these settings 2 | # copy this file to "local-test.properties" in the same directory 3 | # values in that file will override these 4 | # and it will be ignored by git automatically 5 | replicationProtocol=http 6 | replicationServer=10.0.2.2 7 | replicationPort=4984 8 | replicationDatabase=cblite-test 9 | replicationAdminUser= 10 | replicationAdminPassword= -------------------------------------------------------------------------------- /src/instrumentTest/java/com/couchbase/lite/testapp/listener/tests/LiteListenerTestCase.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.lite.testapp.listener.tests; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public abstract class LiteListenerTestCase extends TestCase { 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/lite/listener/Credentials.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.lite.listener; 2 | 3 | import java.util.UUID; 4 | 5 | public class Credentials { 6 | 7 | private String login; 8 | private String password; 9 | 10 | public Credentials() { 11 | setRandomUsernamePassword(); 12 | } 13 | 14 | public Credentials(String login, String password) { 15 | this.login = login; 16 | this.password = password; 17 | } 18 | 19 | public String getLogin() { 20 | return login; 21 | } 22 | 23 | public String getPassword() { 24 | return password; 25 | } 26 | 27 | public void setRandomUsernamePassword() { 28 | login = UUID.randomUUID().toString(); 29 | password = UUID.randomUUID().toString(); 30 | } 31 | 32 | public boolean empty() { 33 | boolean loginEmpty = (login == null || login.isEmpty()); 34 | boolean passwordEmpty = (password == null || password.isEmpty()); 35 | return loginEmpty && passwordEmpty; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return String.format("%s:%s", login, password); 41 | } 42 | 43 | /** 44 | * Test whether two credentials are equal. 45 | * 46 | * Null/empty fields are considered equal to other null/empty fields. 47 | * 48 | * @return 49 | */ 50 | @Override 51 | public boolean equals(Object o) { 52 | 53 | if (o instanceof Credentials) { 54 | Credentials otherCreds = (Credentials) o; 55 | 56 | boolean sameLogin = false; 57 | boolean samePassword = false; 58 | 59 | String otherCredsLogin = otherCreds.getLogin(); 60 | String otherCredsPassword = otherCreds.getPassword(); 61 | 62 | if (otherCredsLogin == null || otherCredsLogin.isEmpty()) { 63 | sameLogin = (getLogin() == null || getLogin().isEmpty()); 64 | } else { 65 | sameLogin = getLogin().equals(otherCredsLogin); 66 | } 67 | 68 | if (otherCredsPassword == null || otherCredsPassword.isEmpty()) { 69 | samePassword = (getPassword() == null || getPassword().isEmpty()); 70 | } else { 71 | samePassword = getPassword().equals(otherCredsPassword); 72 | } 73 | 74 | return sameLogin && samePassword; 75 | } 76 | return super.equals(o); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/lite/listener/LiteListener.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.lite.listener; 2 | 3 | import com.couchbase.lite.Manager; 4 | import com.couchbase.lite.router.URLStreamHandlerFactory; 5 | import com.couchbase.lite.util.Log; 6 | 7 | import java.io.IOException; 8 | import java.net.ServerSocket; 9 | import java.util.Properties; 10 | import java.util.concurrent.ScheduledExecutorService; 11 | 12 | public class LiteListener implements Runnable { 13 | 14 | private static int DEF_SOCKET_TIMEOUT = 30 * 1000; 15 | 16 | private Thread thread; 17 | private Manager manager; 18 | private LiteServer httpServer; 19 | public static final String TAG = "LiteListener"; 20 | private int listenPort; 21 | private int serverStatus; 22 | private int socketTimeout = DEF_SOCKET_TIMEOUT; 23 | 24 | public int getSocketTimeout() { 25 | return socketTimeout; 26 | } 27 | 28 | public void setSocketTimeout(int socketTimeout) { 29 | this.socketTimeout = socketTimeout; 30 | } 31 | 32 | //static inializer to ensure that cblite:// URLs are handled properly 33 | { 34 | URLStreamHandlerFactory.registerSelfIgnoreError(); 35 | } 36 | 37 | /** 38 | * LiteListener constructor 39 | * 40 | * @param manager the Manager instance 41 | * @param suggestedPort the suggested port to use. if not available, will hunt for a new port. 42 | * and this port can be discovered by calling getListenPort() 43 | * @param allowedCredentials any clients connecting to this liteserv must present these 44 | * credentials. 45 | */ 46 | 47 | public LiteListener(Manager manager, int suggestedPort, Credentials allowedCredentials, Properties tjwsProperties) { 48 | this.manager = manager; 49 | this.httpServer = new LiteServer(); 50 | this.httpServer.setProps(tjwsProperties); 51 | this.httpServer.setManager(manager); 52 | this.httpServer.setListener(this); 53 | this.listenPort = discoverEmptyPort(suggestedPort); 54 | this.httpServer.setPort(this.listenPort); 55 | this.httpServer.setAllowedCredentials(allowedCredentials); 56 | } 57 | 58 | public LiteListener(Manager manager, int suggestedPort, Credentials allowedCredentials) { 59 | this(manager, suggestedPort, allowedCredentials, new Properties()); 60 | } 61 | 62 | /** 63 | * Hunt for an empty port starting from startPort by binding a server 64 | * socket until it succeeds w/o getting an exception. Once found, close 65 | * the server socket so that port is available. 66 | * 67 | * Caveat: yes, there is a tiny race condition in the sense that the 68 | * caller could receive a port that was bound by another thread 69 | * before it has a chance to bind) 70 | * 71 | * @param startPort - the port to start hunting at, eg: 5984 72 | * @return the port that was bound. (or a runtime exception is thrown) 73 | */ 74 | public int discoverEmptyPort(int startPort) { 75 | for (int curPort=startPort; curPort<65536; curPort++) { 76 | try { 77 | ServerSocket socket = new ServerSocket(curPort); 78 | socket.close(); 79 | return curPort; 80 | } catch (IOException e) { 81 | Log.w(LiteListener.TAG, "Could not bind to port: %d. Trying another port.", curPort); 82 | } 83 | 84 | } 85 | throw new RuntimeException("Could not find empty port starting from: " + startPort); 86 | } 87 | 88 | @Override 89 | public void run() { 90 | this.serverStatus = httpServer.serve(); 91 | } 92 | 93 | public void start() { 94 | thread = new Thread(this); 95 | thread.start(); 96 | } 97 | 98 | public void stop() { 99 | httpServer.notifyStop(); 100 | } 101 | 102 | public void onServerThread(Runnable r) { 103 | ScheduledExecutorService workExecutor = manager.getWorkExecutor(); 104 | workExecutor.submit(r); 105 | } 106 | 107 | public int serverStatus() { 108 | return this.serverStatus; 109 | } 110 | 111 | public int getListenPort() { 112 | return listenPort; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/lite/listener/LiteServer.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.lite.listener; 2 | 3 | import com.couchbase.lite.Manager; 4 | 5 | import java.util.Properties; 6 | 7 | import Acme.Serve.Serve; 8 | 9 | @SuppressWarnings("serial") 10 | public class LiteServer extends Serve { 11 | 12 | public static final String CBLServer_KEY = "CBLServerInternal"; 13 | public static final String CBL_URI_SCHEME = "cblite://"; 14 | 15 | private Properties props; 16 | private Manager manager; 17 | private LiteListener listener; 18 | 19 | // if this is non-null, then users must present BasicAuth 20 | // credential in every request which matches up with allowedCredentials, 21 | // or else the request will be refused. 22 | // REF: https://github.com/couchbase/couchbase-lite-java-listener/issues/35 23 | private Credentials allowedCredentials; 24 | 25 | public LiteServer() { 26 | props = new Properties(); 27 | } 28 | 29 | public void setManager(Manager manager) { 30 | this.manager = manager; 31 | } 32 | 33 | public void setListener(LiteListener listener) { 34 | this.listener = listener; 35 | } 36 | 37 | public void setProps(Properties properties) { 38 | this.props = properties; 39 | } 40 | 41 | public void setAllowedCredentials(Credentials allowedCredentials) { 42 | this.allowedCredentials = allowedCredentials; 43 | } 44 | 45 | public void setPort(int port) { 46 | props.put("port", port); 47 | } 48 | 49 | @Override 50 | public int serve() { 51 | //pass our custom properties in 52 | this.arguments = props; 53 | 54 | //pass in the CBLServerInternal to the servlet 55 | LiteServlet servlet = new LiteServlet(); 56 | servlet.setManager(manager); 57 | servlet.setListener(listener); 58 | if (allowedCredentials != null) { 59 | servlet.setAllowedCredentials(allowedCredentials); 60 | } 61 | 62 | this.addServlet("/", servlet); 63 | return super.serve(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/lite/listener/LiteServlet.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.lite.listener; 2 | 3 | import com.couchbase.lite.Manager; 4 | import com.couchbase.lite.router.Router; 5 | import com.couchbase.lite.router.RouterCallbackBlock; 6 | import com.couchbase.lite.router.URLConnection; 7 | import com.couchbase.lite.support.Base64; 8 | import com.couchbase.lite.util.Log; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.lang.reflect.Field; 13 | import java.net.MalformedURLException; 14 | import java.net.Socket; 15 | import java.net.URL; 16 | import java.util.Enumeration; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.StringTokenizer; 20 | import java.util.concurrent.CountDownLatch; 21 | 22 | import javax.servlet.ServletException; 23 | import javax.servlet.ServletOutputStream; 24 | import javax.servlet.http.HttpServlet; 25 | import javax.servlet.http.HttpServletRequest; 26 | import javax.servlet.http.HttpServletResponse; 27 | 28 | @SuppressWarnings("serial") 29 | public class LiteServlet extends HttpServlet { 30 | 31 | private Manager manager; 32 | private LiteListener listener; 33 | 34 | // if this is non-null, then users must present BasicAuth 35 | // credential in every request which matches up with allowedCredentials, 36 | // or else the request will be refused. 37 | // REF: https://github.com/couchbase/couchbase-lite-java-listener/issues/35 38 | private Credentials allowedCredentials; 39 | 40 | public void setManager(Manager manager) { 41 | this.manager = manager; 42 | } 43 | 44 | public void setListener(LiteListener listener) { 45 | this.listener = listener; 46 | } 47 | 48 | public void setAllowedCredentials(Credentials allowedCredentials) { 49 | this.allowedCredentials = allowedCredentials; 50 | } 51 | 52 | @Override 53 | public void service(HttpServletRequest request, final HttpServletResponse response) 54 | throws ServletException, IOException { 55 | 56 | if (request != null) 57 | Log.i(Log.TAG_LISTENER, "[REQUEST] (%s) %s: %s", 58 | Thread.currentThread().getName(), 59 | request.getMethod(), 60 | getFullURL(request)); 61 | 62 | Credentials requestCredentials = credentialsWithBasicAuthentication(request); 63 | 64 | if (allowedCredentials != null && !allowedCredentials.empty()) { 65 | if (requestCredentials == null || !requestCredentials.equals(allowedCredentials)) { 66 | Log.d(Log.TAG_LISTENER, "Unauthorized -- requestCredentials not given or do not match allowed credentials"); 67 | response.setHeader("WWW-Authenticate", "Basic realm=\"Couchbase Lite\""); 68 | response.setStatus(401); 69 | return; 70 | } 71 | Log.v(Log.TAG_LISTENER, "Authorized via basic auth"); 72 | } else { 73 | Log.v(Log.TAG_LISTENER, "Not enforcing basic auth -- allowedCredentials null or empty"); 74 | } 75 | 76 | //set path 77 | String urlString = request.getRequestURI(); 78 | String queryString = request.getQueryString(); 79 | if(queryString != null) { 80 | urlString += "?" + queryString; 81 | } 82 | URL url = new URL(LiteServer.CBL_URI_SCHEME + urlString); 83 | final URLConnection conn = (URLConnection)url.openConnection(); 84 | conn.setDoOutput(true); 85 | 86 | //set the method 87 | conn.setRequestMethod(request.getMethod()); 88 | 89 | //set the headers 90 | Enumeration headerNames = request.getHeaderNames(); 91 | while(headerNames.hasMoreElements()) { 92 | String headerName = headerNames.nextElement(); 93 | conn.setRequestProperty(headerName, request.getHeader(headerName)); 94 | } 95 | 96 | //set the body 97 | InputStream is = request.getInputStream(); 98 | if(is != null) { 99 | { 100 | // ((Acme.Serve.Serve.ServeInputStream)is).conn.getSocket().setSoTimeout(30); 101 | try { 102 | Field privateServeConnection = Acme.Serve.Serve.ServeInputStream.class.getDeclaredField("conn"); 103 | privateServeConnection.setAccessible(true); 104 | Acme.Serve.Serve.ServeConnection serveConnection = (Acme.Serve.Serve.ServeConnection) privateServeConnection.get(is); 105 | Socket socket = serveConnection.getSocket(); 106 | socket.setSoTimeout(listener.getSocketTimeout()); 107 | } catch (Exception ex) { 108 | Log.e(Log.TAG_LISTENER, "Exception by reflection" , ex); 109 | } 110 | } 111 | conn.setDoInput(true); 112 | conn.setRequestInputStream(is); 113 | } 114 | 115 | final ServletOutputStream os = response.getOutputStream(); 116 | response.setBufferSize(128); 117 | 118 | final CountDownLatch doneSignal = new CountDownLatch(1); 119 | 120 | final Router router = new Router(manager, conn); 121 | 122 | RouterCallbackBlock callbackBlock = new RouterCallbackBlock() { 123 | @Override 124 | public void onResponseReady() { 125 | Log.v(Log.TAG_LISTENER, "RouterCallbackBlock.onResponseReady() START"); 126 | //set the response code 127 | response.setStatus(conn.getResponseCode()); 128 | //add the resonse headers 129 | Map> headers = conn.getHeaderFields(); 130 | if (headers != null) { 131 | for (String headerName : headers.keySet()) { 132 | for (String headerValue : headers.get(headerName)) { 133 | response.addHeader(headerName, headerValue); 134 | } 135 | } 136 | } 137 | doneSignal.countDown(); 138 | Log.v(Log.TAG_LISTENER, "RouterCallbackBlock.onResponseReady() END"); 139 | } 140 | }; 141 | 142 | router.setCallbackBlock(callbackBlock); 143 | 144 | // set remote URL as source 145 | router.setSource(remoteURL(request)); 146 | 147 | router.start(); 148 | 149 | try { 150 | Log.v(Log.TAG_LISTENER, "CountDownLatch.await() START"); 151 | doneSignal.await(); 152 | Log.v(Log.TAG_LISTENER, "CountDownLatch.await() END"); 153 | 154 | InputStream responseInputStream = conn.getResponseInputStream(); 155 | final byte[] buffer = new byte[65536]; 156 | int r; 157 | while ((r = responseInputStream.read(buffer)) > 0) { 158 | os.write(buffer, 0, r); 159 | os.flush(); 160 | response.flushBuffer(); 161 | } 162 | os.close(); 163 | } catch (InterruptedException e) { 164 | Log.e(Log.TAG_LISTENER, "Interrupted waiting for result", e); 165 | } finally { 166 | if (router != null) { 167 | router.stop(); 168 | } 169 | } 170 | } 171 | 172 | public Credentials credentialsWithBasicAuthentication(HttpServletRequest req) { 173 | try { 174 | String authHeader = req.getHeader("Authorization"); 175 | if (authHeader != null) { 176 | StringTokenizer st = new StringTokenizer(authHeader); 177 | if (st.hasMoreTokens()) { 178 | String basic = st.nextToken(); 179 | if (basic.equalsIgnoreCase("Basic")) { 180 | try { 181 | String credentials = new String(Base64.decode(st.nextToken()), "UTF-8"); 182 | Log.v(Log.TAG_LISTENER, "Credentials: ", credentials); 183 | int p = credentials.indexOf(":"); 184 | if (p != -1) { 185 | String login = credentials.substring(0, p).trim(); 186 | String password = credentials.substring(p + 1).trim(); 187 | 188 | return new Credentials(login, password); 189 | } else { 190 | Log.e(Log.TAG_LISTENER, "Invalid authentication token"); 191 | } 192 | } catch (Exception e) { 193 | Log.w(Log.TAG_LISTENER, "Couldn't retrieve authentication", e); 194 | } 195 | } 196 | } 197 | } else { 198 | Log.d(Log.TAG_LISTENER, "authHeader is null"); 199 | } 200 | } catch (Exception e) { 201 | Log.e(Log.TAG_LISTENER, "Exception getting basic auth credentials", e); 202 | } 203 | return null; 204 | } 205 | 206 | private URL remoteURL(HttpServletRequest req) { 207 | String addr = req.getRemoteAddr(); 208 | Log.v(Log.TAG_LISTENER, "remoteURL() addr=" + addr); 209 | if (!"127.0.0.1".equals(addr) && !"::1".equals(addr)) { 210 | // IPv6 211 | if (addr.indexOf(':') >= 0) 212 | addr = String.format("[%s]", addr); 213 | 214 | String username = allowedCredentials != null ? allowedCredentials.getLogin() : null; 215 | String protocol = req.isSecure() ? "https" : "http"; 216 | String spec = null; 217 | if(username != null && username.length() > 0) 218 | spec = String.format("%s://%s@%s/", protocol, username, addr); 219 | else 220 | spec = String.format("%s://%s/", protocol, addr); 221 | try { 222 | return new URL(spec); 223 | } catch (MalformedURLException e) { 224 | Log.w(Log.TAG_LISTENER, "failed to create remote URL", e); 225 | return null; 226 | } 227 | } 228 | return null; 229 | } 230 | 231 | public static String getFullURL(HttpServletRequest request) { 232 | StringBuffer requestURL = request.getRequestURL(); 233 | String queryString = request.getQueryString(); 234 | 235 | if (queryString == null) { 236 | return requestURL.toString(); 237 | } else { 238 | return requestURL.append('?').append(queryString).toString(); 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/couchbase/couchbase-lite-java-listener/b803ce2a88455dc515aeccbd11d369e434cbcc26/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/couchbase/couchbase-lite-java-listener/b803ce2a88455dc515aeccbd11d369e434cbcc26/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/couchbase/couchbase-lite-java-listener/b803ce2a88455dc515aeccbd11d369e434cbcc26/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CBLiteListener 3 | 4 | -------------------------------------------------------------------------------- /src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /vendor/tjws/LICENSE.txt: -------------------------------------------------------------------------------- 1 | (BSD Style License) 2 | 3 | Copyright (C)1996,1998 by Jef Poskanzer . 4 | Copyright (C) 1999-2012 Dmitriy Rogatkin 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of TJWS nor the names of its contributors may be used to endorse 16 | or promote products derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 27 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | -------------------------------------------------------------------------------- /vendor/tjws/README.md: -------------------------------------------------------------------------------- 1 | # TJWS 2 | 3 | ## Project Page 4 | http://tjws.sourceforge.net/ 5 | 6 | ## github repo 7 | https://github.com/drogatkin/TJWS2 8 | 9 | ## commit hash value CBL adopted 10 | https://github.com/drogatkin/TJWS2/commit/a50a6eca875371f11955ad4fd8f94958e39ff0fc 11 | 12 | # Changes from original commit 13 | - two method signatures are modfied to make the code compilable. 14 | 15 | https://github.com/drogatkin/TJWS2/blob/master/1.x/src/Acme/Serve/Serve.java#L443 16 | https://github.com/drogatkin/TJWS2/blob/master/1.x/src/Acme/Serve/Serve.java#L518 17 | 18 | from: 19 | ```java 20 | public javax.servlet.ServletRegistration.Dynamic addServlet(String urlPat, String className) { 21 | public javax.servlet.ServletRegistration.Dynamic addServlet(String urlPat, Servlet servlet) { 22 | ``` 23 | to: 24 | ```java 25 | public void addServlet(String urlPat, String className) { 26 | public void addServlet(String urlPat, Servlet servlet) { 27 | ``` 28 | 29 | # Notes 30 | - Previously CBL depends on TJWS jar file. Instead, couchbase-lite-java-listener incorporates tjws codes. CBL only incorporates `Acme.Serve` package classes. 31 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Resource/mime.properties: -------------------------------------------------------------------------------- 1 | # $Id: mime.properties,v 1.4 2012/08/13 04:10:58 dmitriy Exp $ 2 | HTML=text/html 3 | HTM=text/html 4 | TXT=text/plain 5 | XML=text/xml 6 | CSS=text/css 7 | SGML=text/x-sgml 8 | SGM=text/x-sgml 9 | GIF=image/gif 10 | JPG=image/jpeg 11 | JPEG=image/jpeg 12 | JPE=image/jpeg 13 | PNG=image/png 14 | BMP=image/bmp 15 | TIF=image/tiff 16 | TIFF=image/tiff 17 | RGB=image/x-rgb 18 | XPM=image/x-xpixmap 19 | XBM=image/x-xbitmap 20 | SVG=image/svg-xml 21 | SVGZ=image/svg-xml 22 | ICO=image/vnd.microsoft.icon 23 | # Audio 24 | AU=audio/basic 25 | SND=audio/basic 26 | MID=audio/mid 27 | MIDI=audio/mid 28 | RMI=audio/mid 29 | KAR=audio/mid 30 | MPGA=audio/mpeg 31 | MP2=audio/mpeg 32 | MP3=audio/mpeg 33 | WAV=audio/wav 34 | AIFF=audio/aiff 35 | AIFC=audio/aiff 36 | AIF=audio/x-aiff 37 | RA=audio/x-realaudio 38 | RPM=audio/x-pn-realaudio-plugin 39 | RAM=audio/x-pn-realaudio 40 | SD2=audio/x-sd2 41 | # Applications 42 | BIN=application/octet-stream 43 | DMS=application/octet-stream 44 | LHA=application/octet-stream 45 | LZH=application/octet-stream 46 | EXE=application/octet-stream 47 | DLL=application/octet-stream 48 | CLASS=application/octet-stream 49 | HQX=application/mac-binhex40 50 | PS=application/postscript 51 | AI=application/postscript 52 | EPS=application/postscript 53 | PDF=application/pdf 54 | RTF=application/rtf 55 | DOC=application/msword 56 | DOCX=application/msword 57 | PPT=application/powerpoint 58 | PPTX=application/powerpoint 59 | FIF=application/fractals 60 | P7C=application/pkcs7-mime 61 | # Application/x 62 | JS=application/x-javascript 63 | Z=application/x-compress 64 | GZ=application/x-gzip 65 | TAR=application/x-tar 66 | TGZ=application/x-compressed 67 | ZIP=application/x-zip-compressed 68 | DIR=application/x-director 69 | DCR=application/x-director 70 | DXR=application/x-director 71 | DVI=application/x-dvi 72 | TEX=application/x-tex 73 | LATEX=application/x-latex 74 | TCL=application/x-tcl 75 | CER=application/x-x509-ca-cert 76 | CRT=application/x-x509-ca-cert 77 | DER=application/x-x509-ca-cert 78 | ISO=application/x-iso9660-image 79 | OTF= font/opentype 80 | TTF= font/truetype 81 | EOT=application/vnd.ms-fontobject 82 | APK=application/vnd.android.package-archive 83 | # Video 84 | MPG=video/mpeg 85 | MPE=video/mpeg 86 | MPEG=video/mpeg 87 | QT=video/quicktime 88 | MOV=video/quicktime 89 | AVI=video/x-msvideo 90 | MOVIE=video/x-sgi-movie 91 | MP4=video/mp4v-es, audio/mp4 92 | # Chemical 93 | PDB=chemical/x-pdb 94 | XYZ=chemical/x-pdb 95 | # X- 96 | ICE=x-conference/x-cooltalk 97 | JNLP=application/x-java-jnlp-file 98 | WRL=x-world/x-vrml 99 | VRML=x-world/x-vrml 100 | WML=text/vnd.wap.wml 101 | WMLC=application/vnd.wap.wmlc 102 | WMLS=text/vnd.wap.wmlscript 103 | WMLSC=application/vnd.wap.wmlscriptc 104 | WBMP=image/vnd.wap.wbmp 105 | FLAC=audio/flac 106 | OGG=audio/ogg 107 | OGA=audio/ogg 108 | SPX=audio/ogg 109 | OGV=video/ogg 110 | OGX=application/ogg -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/CgiServlet.java: -------------------------------------------------------------------------------- 1 | // CgiServlet - runs CGI programs 2 | // 3 | // Copyright (C)1996,1998 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | 29 | package Acme.Serve; 30 | 31 | import java.io.BufferedReader; 32 | 33 | import java.io.File; 34 | import java.io.IOException; 35 | import java.io.InputStream; 36 | import java.io.InputStreamReader; 37 | import java.io.OutputStream; 38 | import java.util.Enumeration; 39 | import java.util.StringTokenizer; 40 | import java.util.Vector; 41 | 42 | import javax.servlet.ServletException; 43 | import javax.servlet.http.HttpServlet; 44 | import javax.servlet.http.HttpServletRequest; 45 | import javax.servlet.http.HttpServletResponse; 46 | import javax.servlet.http.Cookie; 47 | 48 | /// Runs CGI programs. 49 | //

50 | // Note: although many implementations of CGI set the working directory of 51 | // the subprocess to be the directory containing the executable file, the 52 | // CGI spec actually says nothing about the working directory. Since 53 | // Java has no method for setting the working directory, this implementation 54 | // does not set it. 55 | //

56 | // Fetch the software.
57 | // Fetch the entire Acme package. 58 | //

59 | // @see Acme.Serve.Serve 60 | 61 | public class CgiServlet extends HttpServlet { 62 | 63 | // / Returns a string containing information about the author, version, and 64 | // copyright of the servlet. 65 | public String getServletInfo() { 66 | return "TJWS CGI extension $Id: CgiServlet.java,v 1.10 2012/08/24 03:06:34 dmitriy Exp $"; 67 | } 68 | 69 | // / Services a single request from the client. 70 | // @param req the servlet request 71 | // @param req the servlet response 72 | // @exception ServletException when an exception has occurred 73 | public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { 74 | if (!(req.getMethod().equalsIgnoreCase("get") || req.getMethod().equalsIgnoreCase("post"))) { 75 | res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 76 | return; 77 | } 78 | String pathInfo = req.getPathInfo(); 79 | if (pathInfo == null) 80 | dispatchPathname(req, res, getServletContext().getRealPath(req.getServletPath())); 81 | else 82 | dispatchPathname(req, res, getServletContext().getRealPath(req.getServletPath() + pathInfo)); 83 | } 84 | 85 | private void dispatchPathname(HttpServletRequest req, HttpServletResponse res, String path) throws IOException { 86 | if (new File(path).exists()) 87 | serveFile(req, res, path); 88 | else 89 | res.sendError(HttpServletResponse.SC_NOT_FOUND); 90 | } 91 | 92 | private void serveFile(HttpServletRequest req, HttpServletResponse res, String path) throws IOException { 93 | String queryString = req.getQueryString(); 94 | int contentLength = req.getContentLength(); 95 | int c; 96 | 97 | log("running " + path + "?" + queryString); 98 | 99 | // Make argument list. 100 | String argList[] = (path + (queryString != null && queryString.indexOf("=") == -1 ? "+" + queryString : "")) 101 | .split("\\+"); /* 1.4 */ 102 | 103 | // Make environment list. 104 | Vector envVec = new Vector(); 105 | envVec.addElement(makeEnv("PATH", "/usr/local/bin:/usr/ucb:/bin:/usr/bin")); 106 | envVec.addElement(makeEnv("GATEWAY_INTERFACE", "CGI/1.1")); 107 | envVec.addElement(makeEnv("SERVER_SOFTWARE", getServletContext().getServerInfo())); 108 | envVec.addElement(makeEnv("SERVER_NAME", req.getServerName())); 109 | envVec.addElement(makeEnv("SERVER_PORT", Integer.toString(req.getServerPort()))); 110 | envVec.addElement(makeEnv("REMOTE_ADDR", req.getRemoteAddr())); 111 | envVec.addElement(makeEnv("REMOTE_HOST", req.getRemoteHost())); 112 | envVec.addElement(makeEnv("REQUEST_METHOD", req.getMethod())); 113 | if (contentLength != -1) 114 | envVec.addElement(makeEnv("CONTENT_LENGTH", Integer.toString(contentLength))); 115 | if (req.getContentType() != null) 116 | envVec.addElement(makeEnv("CONTENT_TYPE", req.getContentType())); 117 | envVec.addElement(makeEnv("SCRIPT_NAME", req.getServletPath())); 118 | if (req.getPathInfo() != null) 119 | envVec.addElement(makeEnv("PATH_INFO", req.getPathInfo())); 120 | if (req.getPathTranslated() != null) 121 | envVec.addElement(makeEnv("PATH_TRANSLATED", req.getPathTranslated())); 122 | if (queryString != null) 123 | envVec.addElement(makeEnv("QUERY_STRING", queryString)); 124 | envVec.addElement(makeEnv("SERVER_PROTOCOL", req.getProtocol())); 125 | if (req.getRemoteUser() != null) 126 | envVec.addElement(makeEnv("REMOTE_USER", req.getRemoteUser())); 127 | if (req.getAuthType() != null) 128 | envVec.addElement(makeEnv("AUTH_TYPE", req.getAuthType())); 129 | Enumeration hnEnum = req.getHeaderNames(); 130 | while (hnEnum.hasMoreElements()) { 131 | String name = (String) hnEnum.nextElement(); 132 | String value = req.getHeader(name); 133 | if (value == null) 134 | value = ""; 135 | envVec.addElement(makeEnv("HTTP_" + name.toUpperCase().replace('-', '_'), value)); 136 | } 137 | String envList[] = makeList(envVec); 138 | 139 | // Start the command. 140 | Process proc = Runtime.getRuntime().exec(argList, envList); 141 | OutputStream procOut = proc.getOutputStream(); 142 | BufferedReader procIn = new BufferedReader(new InputStreamReader(proc.getInputStream())); 143 | InputStream procErr = proc.getErrorStream(); 144 | try { 145 | // If it's a POST, copy the request data to the process. 146 | if (req.getMethod().equalsIgnoreCase("post")) { 147 | InputStream reqIn = req.getInputStream(); 148 | for (int i = 0; i < contentLength; ++i) { 149 | c = reqIn.read(); 150 | if (c == -1) 151 | break; 152 | procOut.write(c); 153 | } 154 | procOut.close(); 155 | } 156 | 157 | // Now read the response from the process. 158 | OutputStream resOut = res.getOutputStream(); 159 | // Some of the headers have to be intercepted and handled. 160 | boolean firstLine = true; 161 | while (true) { 162 | String line = procIn.readLine(); 163 | if (line == null) 164 | break; 165 | line = line.trim(); 166 | if (line.equals("")) 167 | break; 168 | int colon = line.indexOf(":"); 169 | if (colon == -1) { 170 | // No colon. If it's the first line, parse it for status. 171 | if (firstLine) { 172 | StringTokenizer tok = new StringTokenizer(line, " "); 173 | try { 174 | switch (tok.countTokens()) { 175 | case 2: 176 | tok.nextToken(); 177 | res.setStatus(Integer.parseInt(tok.nextToken())); 178 | break; 179 | case 3: 180 | tok.nextToken(); 181 | res.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken()); 182 | break; 183 | } 184 | } catch (NumberFormatException ignore) { 185 | } 186 | } else { 187 | // No colon and it's not the first line? Ignore. 188 | } 189 | } else { 190 | // There's a colon. Check for certain special headers. 191 | String name = line.substring(0, colon); 192 | String value = line.substring(colon + 1).trim(); 193 | if (name.equalsIgnoreCase("Status")) { 194 | StringTokenizer tok = new StringTokenizer(value, " "); 195 | try { 196 | switch (tok.countTokens()) { 197 | case 1: 198 | res.setStatus(Integer.parseInt(tok.nextToken())); 199 | break; 200 | case 2: 201 | res.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken()); 202 | break; 203 | } 204 | } catch (NumberFormatException ignore) { 205 | } 206 | } else if (name.equalsIgnoreCase("Content-type")) { 207 | res.setContentType(value); 208 | } else if (name.equalsIgnoreCase("Content-length")) { 209 | try { 210 | res.setContentLength(Integer.parseInt(value)); 211 | } catch (NumberFormatException ignore) { 212 | } 213 | } else if (name.equalsIgnoreCase("Location")) { 214 | res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); 215 | res.setHeader(name, value); 216 | } else if (name.equalsIgnoreCase(Serve.ServeConnection.SETCOOKIE)) { 217 | int x = value.indexOf("="); 218 | if (x > 0) { 219 | String n = value.substring(0, x); 220 | String v = value.substring(x + 1).trim(); 221 | res.addCookie(new Cookie(n, v)); 222 | } 223 | } else { 224 | // Not a special header. Just set it. 225 | res.setHeader(name, value); 226 | } 227 | } 228 | } 229 | // Copy the rest of the data uninterpreted. 230 | Acme.Utils.copyStream(procIn, resOut, null); 231 | } catch (IOException e) { 232 | // res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ); 233 | // There's some weird bug in Java, when reading from a Process 234 | // you get a spurious IOException. We have to ignore it. 235 | } finally { 236 | try { 237 | procOut.close(); 238 | } catch (IOException e) { 239 | } 240 | try { 241 | procIn.close(); 242 | } catch (IOException e) { 243 | } 244 | try { 245 | procErr.close(); 246 | } catch (IOException e) { 247 | } 248 | // TODO make it configurable 249 | //proc.destroy(); 250 | } 251 | } 252 | 253 | private static String makeEnv(String name, String value) { 254 | return name + "=" + value; 255 | } 256 | 257 | private static String[] makeList(Vector vec) { 258 | String list[] = new String[vec.size()]; 259 | for (int i = 0; i < vec.size(); ++i) 260 | list[i] = (String) vec.elementAt(i); 261 | return list; 262 | } 263 | 264 | } 265 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/FileServlet.java: -------------------------------------------------------------------------------- 1 | // FileServlet - servlet similar to a standard httpd 2 | // 3 | // Copyright (C)1996,1998 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | // 29 | // All enhancements Copyright (C)1998-2005 by Dmitriy Rogatkin 30 | // http://tjws.sourceforge.net 31 | 32 | package Acme.Serve; 33 | 34 | import java.io.BufferedOutputStream; 35 | import java.io.File; 36 | import java.io.FileInputStream; 37 | import java.io.IOException; 38 | import java.io.InputStream; 39 | import java.io.OutputStream; 40 | import java.io.PrintStream; 41 | import java.lang.reflect.InvocationTargetException; 42 | import java.lang.reflect.Method; 43 | import java.net.URLEncoder; 44 | import java.text.DecimalFormat; 45 | import java.util.Arrays; 46 | import java.util.Date; 47 | import java.util.Enumeration; 48 | import java.util.zip.GZIPOutputStream; //import java.util.zip.DeflaterOutputStream; 49 | 50 | import javax.servlet.ServletException; 51 | import javax.servlet.http.HttpServlet; 52 | import javax.servlet.http.HttpServletRequest; 53 | import javax.servlet.http.HttpServletResponse; 54 | 55 | import Acme.Utils; 56 | 57 | /// Servlet similar to a standard httpd. 58 | //

59 | // Implements the "GET" and "HEAD" methods for files and directories. 60 | // Handles index.html, index.htm, default.htm, default.html. 61 | // Redirects directory URLs that lack a trailing /. 62 | // Handles If-Modified-Since. 63 | //

64 | // Fetch the software.
65 | // Fetch the entire Acme package. 66 | //

67 | // @see Acme.Serve.Serve 68 | 69 | public class FileServlet extends HttpServlet { 70 | public static final String DEF_USE_COMPRESSION = "tjws.fileservlet.usecompression"; 71 | 72 | public static final String DEF_USE_SUPRESSIND = "tjws.fileservlet.suppressindex"; 73 | 74 | // We keep a single throttle table for all instances of the servlet. 75 | // Normally there is only one instance; the exception is subclasses. 76 | static Acme.WildcardDictionary throttleTab = null; 77 | 78 | static final String[] DEFAULTINDEXPAGES = { "index.html", "index.htm", "default.htm", "default.html", "index.php", "index.jsp" }; 79 | 80 | static final DecimalFormat lengthftm = new DecimalFormat("#"); 81 | 82 | static final String BYTES_UNIT = "bytes"; 83 | 84 | protected String charSet = Serve.UTF8;// "iso-8859-1"; 85 | 86 | private static final boolean logenabled = false; 87 | 88 | // true; 89 | 90 | private Method canExecute, getFreeSpace; // TODO implement free space 91 | 92 | private boolean useCompression; 93 | 94 | private boolean supressIndex; 95 | 96 | // / Constructor. 97 | public FileServlet() { 98 | try { 99 | canExecute = File.class.getMethod("canExecute", Utils.EMPTY_CLASSES); 100 | } catch (SecurityException e) { 101 | } catch (NoSuchMethodException e) { 102 | } 103 | try { 104 | getFreeSpace = File.class.getMethod("getFreeSpace", Utils.EMPTY_CLASSES); 105 | } catch (SecurityException e) { 106 | } catch (NoSuchMethodException e) { 107 | } 108 | useCompression = System.getProperty(DEF_USE_COMPRESSION) != null; 109 | supressIndex = System.getProperty(DEF_USE_SUPRESSIND) != null; 110 | } 111 | 112 | // / Constructor with throttling. 113 | // @param throttles filename containing throttle settings 114 | // @param charset used for displaying directory page 115 | // @see ThrottledOutputStream 116 | public FileServlet(String throttles, String charset) throws IOException { 117 | this(); 118 | if (charset != null) 119 | this.charSet = charset; 120 | readThrottles(throttles); 121 | } 122 | 123 | private void readThrottles(String throttles) throws IOException { 124 | Acme.WildcardDictionary newThrottleTab = ThrottledOutputStream.parseThrottleFile(throttles); 125 | if (throttleTab == null) 126 | throttleTab = newThrottleTab; 127 | else { 128 | // Merge the new one into the old one. 129 | Enumeration keys = newThrottleTab.keys(); 130 | Enumeration elements = newThrottleTab.elements(); 131 | while (keys.hasMoreElements()) { 132 | Object key = keys.nextElement(); 133 | Object element = elements.nextElement(); 134 | throttleTab.put(key, element); 135 | } 136 | } 137 | } 138 | 139 | // / Returns a string containing information about the author, version, and 140 | // copyright of the servlet. 141 | public String getServletInfo() { 142 | return "TJWS: File servlet similar to httpd"; 143 | } 144 | 145 | // / Services a single request from the client. 146 | // @param req the servlet request 147 | // @param req the servlet response 148 | // @exception ServletException when an exception has occurred 149 | public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { 150 | boolean headOnly; 151 | if (req.getMethod().equalsIgnoreCase("get") || req.getAttribute("javax.servlet.forward.request_uri") != null 152 | || req.getAttribute("javax.servlet.include.request_uri") != null) 153 | headOnly = false; 154 | else if (req.getMethod().equalsIgnoreCase("head")) 155 | headOnly = true; 156 | else { 157 | res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "Method "+req.getMethod()); 158 | return; 159 | } 160 | req.setCharacterEncoding(Serve.UTF8); 161 | String path = Utils.canonicalizePath(req.getPathInfo()); 162 | log("Canonical path:"+path+" for "+req.getPathInfo()+" and servelt path "+req.getServletPath()); 163 | //res.setBufferSize(Utils.COPY_BUF_SIZE/2); 164 | dispatchPathname(req, res, headOnly, path); 165 | } 166 | 167 | private void dispatchPathname(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path) 168 | throws IOException { 169 | log("path trans: " + req.getPathTranslated()); 170 | String filename = req.getPathTranslated() != null ? req.getPathTranslated().replace('/', File.separatorChar) 171 | : ""; 172 | File file = new File(filename); 173 | log("retrieving '" + filename + "' for path " + path); 174 | if (file.exists()) { 175 | if (!file.isDirectory()) 176 | serveFile(req, res, headOnly, path, file); 177 | else { 178 | log("showing dir " + file); 179 | if (redirectDirectory(req, res, path, file) == false) 180 | showIdexFile(req, res, headOnly, path, filename); 181 | } 182 | } else 183 | res.sendError(HttpServletResponse.SC_NOT_FOUND, file.getName()+" not found"); 184 | } 185 | 186 | private void showIdexFile(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, 187 | String parent) throws IOException { 188 | log("showing index in directory " + parent); 189 | for (int i = 0; i < DEFAULTINDEXPAGES.length; i++) { 190 | File indexFile = new File(parent, DEFAULTINDEXPAGES[i]); 191 | if (indexFile.exists()) { 192 | serveFile(req, res, headOnly, path, indexFile); 193 | return; 194 | } 195 | } 196 | // index not found 197 | if (supressIndex) 198 | res.sendError(HttpServletResponse.SC_NOT_FOUND, "No index file found"); 199 | else 200 | serveDirectory(req, res, headOnly, path, new File(parent)); 201 | } 202 | 203 | private void serveFile(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, File file) 204 | throws IOException { 205 | log("getting " + file); 206 | if (logenabled) { 207 | Enumeration enh = req.getHeaderNames(); 208 | while (enh.hasMoreElements()) { 209 | String hn = (String) enh.nextElement(); 210 | log("hdr:" + hn + ":" + req.getHeader(hn)); 211 | } 212 | } 213 | if (!file.canRead()) { 214 | res.sendError(HttpServletResponse.SC_FORBIDDEN); 215 | return; 216 | } else 217 | // by Niel Markwick 218 | try { 219 | file.getCanonicalPath(); 220 | } catch (Exception e) { 221 | res.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden, exception:" + e); 222 | return; 223 | } 224 | 225 | // Handle If-Modified-Since. 226 | res.setStatus(HttpServletResponse.SC_OK); 227 | long lastMod = file.lastModified(); 228 | long ifModSince = req.getDateHeader("If-Modified-Since"); 229 | boolean noContLen = false; 230 | if (ifModSince != -1 && ifModSince >= lastMod) { 231 | res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 232 | headOnly = true; 233 | noContLen = true; 234 | } 235 | // TODO add processing If-None-Match, If-Unmodified-Since and If-Match 236 | String contentType = getServletContext().getMimeType(file.getName()); 237 | if (contentType != null) 238 | res.setContentType(contentType); 239 | long flen = file.length(); 240 | // check for range 241 | String range = req.getHeader("Range"); 242 | long sr = 0; 243 | long er = -1; 244 | if (range != null) { 245 | log("Range:" + range); 246 | if (range.regionMatches(true, 0, BYTES_UNIT, 0, BYTES_UNIT.length())) { 247 | int i = range.indexOf('-'); 248 | if (i > 0) { 249 | try { 250 | sr = Long.parseLong(range.substring(BYTES_UNIT.length() + 1, i)); 251 | if (sr < 0) 252 | throw new NumberFormatException("Invalid start range value:" + sr); 253 | try { 254 | er = Long.parseLong(range.substring(i + 1)); 255 | } catch (NumberFormatException nfe) { 256 | er = flen - 1; 257 | } 258 | } catch (NumberFormatException nfe) { 259 | 260 | } 261 | } // else invalid range? ignore? 262 | } // else other units not supported 263 | log("range values " + sr + " to " + er); 264 | } else { 265 | res.setHeader("Accept-Ranges", "bytes"); // by Alexander Ryumshin 266 | } 267 | long clen = er < 0 ? flen : (er - sr + 1); 268 | res.setDateHeader("Last-modified", lastMod); 269 | 270 | if (er > 0) { 271 | if (sr > er || er >= flen) { 272 | // TODO If-Range presence can change behavior 273 | res.setHeader("Content-Range", BYTES_UNIT + " */" + flen); 274 | res.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 275 | return; 276 | } 277 | res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); 278 | res.setHeader("Content-Range", BYTES_UNIT + " " + sr + '-' + er + '/' + flen); 279 | log("content-range:" + BYTES_UNIT + " " + sr + '-' + er + '/' + flen); 280 | } 281 | // String ifRange = req.getHeader("If-Range"); 282 | // res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); 283 | boolean doCompress = false; 284 | if (useCompression && contentType != null && contentType.startsWith("text")) { 285 | if (Utils.isGzipAccepted(req.getHeader("Accept-Encoding")) > 0) { 286 | res.setHeader("Content-Encoding", "gzip"); 287 | doCompress = true; 288 | } 289 | } 290 | if ((doCompress == false || headOnly) && !noContLen) { 291 | if (clen < Integer.MAX_VALUE) 292 | res.setContentLength((int) clen); 293 | else 294 | res.setHeader("Content-Length", Long.toString(clen)); 295 | } 296 | 297 | String dnla = req.getHeader("GetContentFeatures.DLNA.ORG"); // check also Pragma: getIfoFileURI.dlna.org 298 | if ("1".equals(dnla)) { 299 | res.setHeader("transferMode.dlna.org", "Streaming"); 300 | //res.setHeader("contentFeatures.dlna.org", "DLNA.ORG_OP=00;DLNA.ORG_CI=0"); 301 | //res.setHeader("",""); 302 | } 303 | OutputStream out = null; 304 | InputStream in = null; 305 | try { 306 | if (!headOnly) { 307 | out = doCompress ? new GZIPOutputStream(res.getOutputStream()) : (OutputStream) res.getOutputStream(); 308 | // Check throttle. 309 | if (throttleTab != null) { 310 | ThrottleItem throttleItem = (ThrottleItem) throttleTab.get(path); 311 | if (throttleItem != null) { 312 | // !!! Need to count for multiple simultaneous fetches. 313 | out = new ThrottledOutputStream(out, throttleItem.getMaxBps()); 314 | } 315 | } 316 | 317 | in = new FileInputStream(file); 318 | while (sr > 0) { 319 | long sl = in.skip(sr); 320 | if (sl > 0) 321 | sr -= sl; 322 | else { 323 | res.sendError(HttpServletResponse.SC_CONFLICT, "Conflict"); 324 | // better can be Internal Server Error 325 | return; 326 | } 327 | } 328 | copyStream(in, out, clen); 329 | if (doCompress) 330 | ((GZIPOutputStream) out).finish(); 331 | } 332 | } finally { 333 | if (in != null) 334 | try { 335 | in.close(); 336 | } catch (IOException ioe) { 337 | } 338 | if (out != null) { 339 | out.flush(); 340 | out.close(); 341 | } 342 | } 343 | } 344 | 345 | // / Copy a file from in to out. 346 | // Sub-classes can override this in order to do filtering of some sort. 347 | public void copyStream(InputStream in, OutputStream out, long len) throws IOException { 348 | Acme.Utils.copyStream(in, out, len); 349 | } 350 | 351 | private void serveDirectory(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, 352 | File file) throws IOException { 353 | log("indexing " + file); 354 | if (!file.canRead()) { 355 | res.sendError(HttpServletResponse.SC_FORBIDDEN); 356 | return; 357 | } 358 | res.setStatus(HttpServletResponse.SC_OK); 359 | res.setContentType("text/html;charset=" + charSet); 360 | OutputStream out = res.getOutputStream(); 361 | if (!headOnly) { 362 | String[] names = file.list(); 363 | if (names == null) { 364 | res.sendError(HttpServletResponse.SC_FORBIDDEN, "Can't access " + req.getRequestURI()); 365 | return; 366 | } 367 | PrintStream p = new PrintStream(new BufferedOutputStream(out), false, charSet); // 1.4 368 | p.println(""); 369 | p.println(""); 370 | p.println("Index of " + path + ""); 371 | p.println("

Index of " + path + "

"); 373 | p.println("
");
374 | 			p.println("mode         bytes  last-changed    name");
375 | 			p.println("
"); 376 | // TODO consider not case sensetive search 377 | Arrays.sort(names); 378 | long total = 0; 379 | for (int i = 0; i < names.length; ++i) { 380 | File aFile = new File(file, names[i]); 381 | String aFileType; 382 | long aFileLen; 383 | if (aFile.isDirectory()) 384 | aFileType = "d"; 385 | else if (aFile.isFile()) 386 | aFileType = "-"; 387 | else 388 | aFileType = "?"; 389 | String aFileRead = (aFile.canRead() ? "r" : "-"); 390 | String aFileWrite = (aFile.canWrite() ? "w" : "-"); 391 | String aFileExe = "-"; 392 | if (canExecute != null) 393 | try { 394 | if (((Boolean) canExecute.invoke(aFile, Utils.EMPTY_OBJECTS)).booleanValue()) 395 | aFileExe = "x"; 396 | } catch (IllegalArgumentException e) { 397 | } catch (IllegalAccessException e) { 398 | } catch (InvocationTargetException e) { 399 | } 400 | String aFileSize = lengthftm.format(aFileLen = aFile.length()); 401 | total += (aFileLen + 1023) / 1024; // 402 | while (aFileSize.length() < 12) 403 | aFileSize = " " + aFileSize; 404 | String aFileDate = Acme.Utils.lsDateStr(new Date(aFile.lastModified())); 405 | while (aFileDate.length() < 14) 406 | aFileDate += " "; 407 | String aFileDirsuf = (aFile.isDirectory() ? "/" : ""); 408 | String aFileSuf = (aFile.isDirectory() ? "/" : ""); 409 | // TODO HTML encode file name 410 | p.println(aFileType + aFileRead + aFileWrite + aFileExe + " " + aFileSize + " " + aFileDate + " " 411 | + "" + Utils.htmlEncode(names[i], false) + aFileSuf + ""); 413 | } 414 | if (total != 0) 415 | total += 3; 416 | p.println("total " + total); 417 | p.println("
"); 418 | p.println("
"); 419 | p.print(Serve.Identification.serverIdHtml); 420 | p.println(""); 421 | p.flush(); 422 | } 423 | out.close(); 424 | } 425 | 426 | /** 427 | * 428 | * @param req 429 | * http request 430 | * @param res 431 | * http response 432 | * @param path 433 | * web path 434 | * @param file 435 | * file system path 436 | * @return true if redirection required and happened 437 | * @throws IOException 438 | * in redirection 439 | */ 440 | private boolean redirectDirectory(HttpServletRequest req, HttpServletResponse res, String path, File file) 441 | throws IOException { 442 | int pl = path.length(); 443 | if (pl > 0 && path.charAt(pl - 1) != '/') { 444 | // relative redirect 445 | int sp = path.lastIndexOf('/'); 446 | if (sp < 0) 447 | path += '/'; 448 | else 449 | path = path.substring(sp + 1) + '/'; 450 | log("redirecting dir " + path); 451 | res.sendRedirect(path); 452 | return true; 453 | } 454 | return false; 455 | } 456 | 457 | //@Override 458 | public void log(String msg) { 459 | if (logenabled) 460 | super.log(msg); 461 | } 462 | } -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/Main.java: -------------------------------------------------------------------------------- 1 | /* tjws - Main.java 2 | * Copyright (C) 1999-2007 Dmitriy Rogatkin. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 15 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 17 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 18 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 19 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 20 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 21 | * SUCH DAMAGE. 22 | * 23 | * Visit http://tjws.sourceforge.net to get the latest information 24 | * about Rogatkin's products. 25 | * $Id: Main.java,v 1.25 2013/07/24 06:20:37 cvs Exp $ 26 | * Created on Feb 22, 2007 27 | * @author Dmitriy 28 | */ 29 | package Acme.Serve; 30 | 31 | import java.io.BufferedReader; 32 | import java.io.File; 33 | import java.io.FileInputStream; 34 | import java.io.FileOutputStream; 35 | import java.io.FileReader; 36 | import java.io.FilterOutputStream; 37 | import java.io.IOException; 38 | import java.io.InputStreamReader; 39 | import java.io.OutputStream; 40 | import java.io.PrintStream; 41 | import java.text.DecimalFormat; 42 | import java.util.Enumeration; 43 | import java.util.HashMap; 44 | import java.util.Hashtable; 45 | import java.util.Map; 46 | import java.util.NoSuchElementException; 47 | import java.util.StringTokenizer; 48 | 49 | import Acme.Utils; 50 | 51 | public class Main extends Serve { 52 | public static final String CLI_FILENAME = "cmdparams"; 53 | 54 | private static final String progName = "Serve"; 55 | 56 | protected static Serve serve; 57 | 58 | private static Thread sdHook; 59 | 60 | /** main entry for standalone run 61 | * 62 | * @param args 63 | */ 64 | public static void main(String[] args) { 65 | //int code = main1(args); 66 | Runtime.getRuntime().halt(main1(args)); 67 | } 68 | 69 | /** 70 | * Main entry for embedded run 71 | * @param args 72 | */ 73 | public static int main1(String[] args) { 74 | String workPath = System.getProperty("user.dir", "."); 75 | StringBuffer messages = null; 76 | 77 | int argc = args.length; 78 | int argn; 79 | if (argc == 0) { // a try to read from file for java -jar server.jar 80 | args = readArguments(workPath, CLI_FILENAME); 81 | if (args == null) { 82 | messages = appendMessage(messages, "Can't read from CLI file ("+CLI_FILENAME+") at "+workPath+"\n"); 83 | } else 84 | argc = args.length; 85 | } 86 | 87 | Map arguments = new HashMap(20); 88 | arguments.put(ARG_WORK_DIRECTORY, workPath); 89 | // Parse args. 90 | // TODO: redesign process of parameters based on a map 91 | for (argn = 0; argn < argc && args[argn].length() > 0 && args[argn].charAt(0) == '-';) { 92 | if (args[argn].equals("-p") && argn + 1 < argc) { 93 | ++argn; 94 | arguments.put(ARG_PORT, new Integer(args[argn])); 95 | } else if (args[argn].equals("-t") && argn + 1 < argc) { 96 | ++argn; 97 | arguments.put(ARG_THROTTLES, args[argn]); 98 | } else if (args[argn].equals("-s") && argn + 1 < argc) { 99 | ++argn; 100 | arguments.put(ARG_SERVLETS, args[argn]); 101 | } else if (args[argn].equals("-r") && argn + 1 < argc) { 102 | ++argn; 103 | arguments.put(ARG_REALMS, args[argn]); 104 | } else if (args[argn].equals("-a") && argn + 1 < argc) { 105 | ++argn; 106 | arguments.put(ARG_ALIASES, args[argn]); 107 | } else if (args[argn].equals("-b") && argn + 1 < argc) { 108 | ++argn; 109 | if (arguments.containsKey(ARG_BINDADDRESS)) 110 | messages = appendMessage(messages, "Multiple usage of a bind address. "+args[argn]+" ignored\n"); 111 | else 112 | arguments.put(ARG_BINDADDRESS, args[argn]); 113 | } else if (args[argn].equals("-k") && argn + 1 < argc) { 114 | ++argn; 115 | arguments.put(ARG_BACKLOG, args[argn]/*new Integer(args[argn])*/); 116 | } else if (args[argn].equals("-j") && argn + 1 < argc) { 117 | ++argn; 118 | arguments.put(ARG_JSP, args[argn]); 119 | } else if (args[argn].equals("-w") && argn + 1 < argc) { 120 | ++argn; 121 | arguments.put(ARG_WAR, args[argn]); 122 | } else if (args[argn].equals("-c") && argn + 1 < argc) { 123 | ++argn; 124 | arguments.put(ARG_CGI_PATH, args[argn]); 125 | } else if (args[argn].equals("-mka") && argn + 1 < argc) { 126 | ++argn; 127 | arguments.put(ARG_MAX_CONN_USE, args[argn]); 128 | arguments.put(ARG_KEEPALIVE, Boolean.TRUE); 129 | } else if (args[argn].equals("-nka")) { 130 | arguments.put(ARG_KEEPALIVE, Boolean.FALSE); 131 | } else if (args[argn].equals("-sp")) { 132 | arguments.put(ARG_SESSION_PERSIST, Boolean.TRUE); 133 | } else if (args[argn].equals("-kat") && argn + 1 < argc) { 134 | ++argn; 135 | arguments.put(ARG_KEEPALIVE_TIMEOUT, args[argn]); 136 | arguments.put(ARG_KEEPALIVE, Boolean.TRUE); 137 | } else if (args[argn].equals("-e") && argn + 1 < argc) { 138 | ++argn; 139 | try { 140 | arguments.put(ARG_SESSION_TIMEOUT, new Integer(args[argn])); 141 | } catch (NumberFormatException nfe) { 142 | } 143 | } else if (args[argn].equals("-z") && argn + 1 < argc) { 144 | ++argn; 145 | arguments.put(ARG_THREAD_POOL_SIZE, args[argn]); 146 | // backlog will be anyway upper limitation 147 | } else if (args[argn].equals("-d") && argn + 1 < argc) { 148 | ++argn; 149 | arguments.put(ARG_LOG_DIR, args[argn]); 150 | } else if (args[argn].startsWith("-l")) { 151 | arguments 152 | .put(ARG_ACCESS_LOG_FMT, 153 | "{0}:{9,number,#} {1} {2} [{3,date,dd/MMM/yyyy:HH:mm:ss Z}] \"{4} {5} {6}\" {7,number,#} {8,number} {10} {11}"); 154 | if (args[argn].length() > 2) { 155 | arguments.put(ARG_LOG_OPTIONS, args[argn].substring(2).toUpperCase()); 156 | if (args[argn].indexOf('f') >= 0 && argn < argc-1) { 157 | ++argn; 158 | arguments.put(ARG_ACCESS_LOG_FMT, args[argn]); 159 | } 160 | } else 161 | arguments.put(ARG_LOG_OPTIONS, ""); 162 | } else if (args[argn].startsWith("-nohup")) { 163 | arguments.put(ARG_NOHUP, ARG_NOHUP); 164 | } else if (args[argn].equals("-m") && argn + 1 < argc) { 165 | ++argn; 166 | try { 167 | arguments.put(ARG_MAX_ACTIVE_SESSIONS, new Integer(args[argn])); 168 | if (((Integer) arguments.get(ARG_MAX_ACTIVE_SESSIONS)).intValue() < DEF_MIN_ACT_SESS) 169 | arguments.put(ARG_MAX_ACTIVE_SESSIONS, new Integer(DEF_MIN_ACT_SESS)); 170 | } catch (NumberFormatException nfe) { 171 | // ignored 172 | } 173 | } else if (args[argn].equals("-err")) { 174 | if (argn + 1 < argc && args[argn + 1].startsWith("-") == false) { 175 | ++argn; 176 | try { 177 | arguments.put(ARG_ERR, (PrintStream) Class.forName(args[argn]).newInstance()); 178 | } catch (Error er) { 179 | messages = appendMessage(messages, 180 | "Problem of processing class parameter of error redirection stream: ").append(er) 181 | .append('\n'); 182 | } catch (Exception ex) { 183 | messages = appendMessage(messages, 184 | "Exception in processing class parameter of error redirection stream: ").append(ex) 185 | .append('\n'); 186 | } 187 | } else 188 | arguments.put(ARG_ERR, System.err); 189 | } else if (args[argn].equals("-out")) { 190 | if (argn + 1 < argc && args[argn + 1].startsWith("-") == false) { 191 | ++argn; 192 | try { 193 | arguments.put(ARG_OUT, (PrintStream) Class.forName(args[argn]).newInstance()); 194 | } catch (Error er) { 195 | messages = appendMessage(messages, 196 | "Problem of processing class parameter of out redirection stream: ").append(er).append( 197 | '\n'); 198 | } catch (Exception ex) { 199 | messages = appendMessage(messages, 200 | "Exception in processing class parameter of out redirection stream: ").append(ex) 201 | .append('\n'); 202 | } 203 | } 204 | } else if (args[argn].equals("-sh")) { 205 | arguments.put(ARG_HTTPONLY_SC, ARG_HTTPONLY_SC); 206 | } else if (args[argn].equals("-ss")) { 207 | arguments.put(ARG_SECUREONLY_SC, ARG_SECUREONLY_SC); 208 | } else if (args[argn].equals("-g") && argn + 1 < argc) { 209 | arguments.put(ARG_LOGROLLING_LINES, Integer.valueOf(args[++argn])); 210 | } else if (args[argn].startsWith("-")) { // free args, note it generate problem since free arguments can match internal arguments 211 | if (args[argn].length() > 1) { 212 | String name = args[argn].substring(1); 213 | if (arguments.containsKey(name)) 214 | messages = appendMessage(messages, "Multiple usage of '-"+name+"'="+args[++argn]+ " ignored\n"); 215 | else 216 | arguments.put(name,// .toUpperCase(), 217 | argn < argc - 1 ? args[++argn] : ""); 218 | //System.out.println("Added free arg:"+args[argn-1]+"="+args[argn]); 219 | } else 220 | messages = appendMessage(messages, "Parameter '-' ignored, perhaps extra blank separator was used.\n"); 221 | } else 222 | usage(); 223 | 224 | ++argn; 225 | } 226 | if (argn != argc) 227 | usage(); 228 | if (System.getProperty(DEF_PROXY_CONFIG) != null) 229 | arguments.put(ARG_PROXY_CONFIG, System.getProperty(DEF_PROXY_CONFIG)); 230 | // log and error stream manipulation 231 | // TODO add log rotation feature, it can be done as plug-in 232 | PrintStream printstream = System.err; 233 | if (arguments.get(ARG_OUT) != null) 234 | printstream = (PrintStream) arguments.get(ARG_OUT); 235 | else { 236 | String logEncoding = System.getProperty(DEF_LOGENCODING); 237 | try { 238 | File logDir = new File(workPath); 239 | if (arguments.get(ARG_LOG_DIR) != null) { 240 | File dir = new File((String) arguments.get(ARG_LOG_DIR)); 241 | if (dir.isAbsolute() == true) { 242 | logDir = dir; 243 | } else { 244 | logDir = new File(workPath, dir.getPath()); 245 | } 246 | } 247 | File logFile = new File(logDir, "TJWS-" + System.currentTimeMillis() + ".log"); 248 | int logRollThreshold = arguments.get(ARG_LOGROLLING_LINES)!=null?((Integer)arguments.get(ARG_LOGROLLING_LINES)).intValue():0; 249 | OutputStream logStream = logRollThreshold>1000?(OutputStream)new RollingOutputStream(logFile, logRollThreshold): (OutputStream)new FileOutputStream(logFile); 250 | if (logEncoding != null) 251 | printstream = new PrintStream(logStream, true, logEncoding); /* 1.4 */ 252 | else 253 | printstream = new PrintStream(logStream, true); 254 | } catch (IOException e) { 255 | System.err.println("I/O problem at setting a log stream " + e); 256 | } 257 | } 258 | if (arguments.get(ARG_ERR) != null) { 259 | System.setErr((PrintStream) arguments.get(ARG_ERR)); 260 | } else { 261 | System.setErr(printstream); 262 | } 263 | if (messages != null) 264 | System.err.println(messages); 265 | /** 266 | * format path mapping from=givenpath;dir=realpath 267 | */ 268 | PathTreeDictionary mappingtable = new PathTreeDictionary(); 269 | if (arguments.get(ARG_ALIASES) != null) { 270 | File file = new File((String) arguments.get(ARG_ALIASES)); 271 | if (file.isAbsolute() == false) 272 | file = new File(workPath, file.getPath()); 273 | if (file.exists() && file.canRead()) { 274 | try { 275 | // DataInputStream in = new DataInputStream( 276 | // new FileInputStream(file)); 277 | BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file))); 278 | do { 279 | String mappingstr = in.readLine(); // no arguments in non ASCII encoding allowed 280 | if (mappingstr == null) 281 | break; 282 | if (mappingstr.startsWith("#")) 283 | continue; 284 | StringTokenizer maptokenzr = new StringTokenizer(mappingstr, "=;"); 285 | if (maptokenzr.hasMoreTokens()) { 286 | if (maptokenzr.nextToken("=").equalsIgnoreCase("from")) { 287 | if (maptokenzr.hasMoreTokens()) { 288 | String srcpath = maptokenzr.nextToken("=;").trim(); 289 | if (maptokenzr.hasMoreTokens() 290 | && maptokenzr.nextToken(";=").equalsIgnoreCase("dir")) 291 | try { 292 | if (maptokenzr.hasMoreTokens()) { 293 | File mapFile = new File(maptokenzr.nextToken()); 294 | if (mapFile.isAbsolute() == false) 295 | mapFile = new File(workPath, mapFile.getPath()); 296 | if (srcpath.endsWith("/*") == false) 297 | if (srcpath.endsWith("/")) 298 | srcpath += "*"; 299 | else 300 | srcpath += "/*"; 301 | if (mapFile.getCanonicalFile().exists()) 302 | mappingtable.put(srcpath, mapFile); 303 | else 304 | System.err.println("TJWS: Mapping file " + mapFile + " (" + srcpath 305 | + ") doesn't exist or not readable."); 306 | } 307 | } catch (NullPointerException e) { 308 | } 309 | } 310 | } 311 | } 312 | } while (true); 313 | } catch (IOException e) { 314 | System.err.println("TJWS: Problem reading aliases file: " + arguments.get(ARG_ALIASES) + "/" + e); 315 | } 316 | } else 317 | System.err.println("TJWS: File " + file + " (" + arguments.get(ARG_ALIASES) 318 | + ") doesn't exist or not readable."); 319 | } 320 | // format realmname=path,user:password,,,, 321 | // TODO consider to add a role, like realmname=path,user:password[:role] 322 | PathTreeDictionary realms = new PathTreeDictionary(); 323 | if (arguments.get(ARG_REALMS) != null) { 324 | try { 325 | File file = new File((String) arguments.get(ARG_REALMS)); 326 | if (file.isAbsolute() == false) 327 | file = new File(workPath, file.getPath()); 328 | BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file))); 329 | 330 | do { 331 | String realmstr = in.readLine(); 332 | if (realmstr == null) 333 | break; 334 | if (realmstr.startsWith("#")) 335 | continue; 336 | StringTokenizer rt = new StringTokenizer(realmstr, "=,:"); 337 | if (rt.hasMoreTokens()) { 338 | String realmname = null; 339 | realmname = rt.nextToken(); 340 | if (rt.hasMoreTokens()) { 341 | String realmPath = null; 342 | realmPath = rt.nextToken(); 343 | if (rt.hasMoreTokens()) { 344 | String user = rt.nextToken(); 345 | if (rt.hasMoreTokens()) { 346 | String password = rt.nextToken(); 347 | BasicAuthRealm realm = null; 348 | Object o[] = realms.get(realmPath); 349 | if (o != null && o[0] != null) 350 | realm = (BasicAuthRealm) o[0]; 351 | else { 352 | realm = new BasicAuthRealm(realmname); 353 | if (realmPath.endsWith("/*") == false) 354 | realmPath+="/*"; 355 | else if (realmPath.endsWith("/")) 356 | realmPath+="*"; 357 | realms.put(realmPath, realm); 358 | } 359 | realm.put(user, password); 360 | } 361 | } 362 | } 363 | } 364 | } while (true); 365 | } catch (IOException ioe) { 366 | System.err.println("TJWS: I/O problem in reading realms file " + arguments.get(ARG_REALMS) + ": " + ioe); 367 | } 368 | } 369 | // Create the server. 370 | serve = new Serve(arguments, printstream); 371 | // can use log(.. after this point 372 | serve.setMappingTable(mappingtable); 373 | serve.setRealms(realms); 374 | File tempFile = arguments.get(ARG_SERVLETS) == null ? null : new File((String) arguments.get(ARG_SERVLETS)); 375 | if (tempFile != null && tempFile.isAbsolute() == false) 376 | tempFile = new File(workPath, tempFile.getPath()); 377 | final File servFile = tempFile; 378 | // TODO analyze possible race condition 379 | if (servFile != null) 380 | new Thread(new Runnable() { 381 | public void run() { 382 | readServlets(servFile); 383 | } 384 | }).start(); 385 | // And add the standard Servlets. 386 | String throttles = (String) arguments.get(ARG_THROTTLES); 387 | if (throttles == null) 388 | serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH)); 389 | else 390 | try { 391 | serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH), throttles); 392 | } catch (IOException e) { 393 | serve.log("Problem reading throttles file: " + e, e); 394 | System.exit(1); 395 | } 396 | serve.addWebsocketProvider((String) arguments.get(ARG_WEBSOCKET)); 397 | serve.addWarDeployer((String) arguments.get(ARG_WAR), throttles); 398 | if (arguments.get(ARG_NOHUP) == null) 399 | new Thread(new Runnable() { 400 | public void run() { 401 | BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 402 | String line; 403 | while (true) { 404 | try { 405 | System.out.print("Press \"q\" , for gracefully stopping the server "); 406 | line = in.readLine(); 407 | if (line != null && line.length() > 0 && line.charAt(0) == 'q') { 408 | serve.notifyStop(); 409 | break; 410 | } 411 | } catch (IOException e) { 412 | serve.log("Exception in reading from console ", e); 413 | break; 414 | } 415 | } 416 | } 417 | }, "Stop Monitor").start(); 418 | else { 419 | Runtime.getRuntime().addShutdownHook(sdHook = new Thread(new Runnable() { 420 | synchronized public void run() { 421 | serve.destroyAllServlets(); 422 | } 423 | }, "ShutDownHook")); 424 | } 425 | // And run. 426 | int code = serve.serve(); 427 | if (code != 0 && arguments.get(ARG_NOHUP) == null) 428 | try { 429 | System.out.println(); 430 | System.in.close(); // to break termination thread 431 | } catch (IOException e) { 432 | } 433 | try { 434 | if (sdHook != null) 435 | Runtime.getRuntime().removeShutdownHook(sdHook); 436 | serve.destroyAllServlets(); 437 | } catch (IllegalStateException ise) { 438 | 439 | } catch (Throwable t) { 440 | if (t instanceof ThreadDeath) 441 | throw (ThreadDeath)t; 442 | serve.log("At destroying ", t); 443 | } 444 | killAliveThreads(); 445 | printstream.close(); 446 | return code; 447 | } 448 | 449 | private static StringBuffer appendMessage(StringBuffer messages, String message) { 450 | if (messages == null) 451 | messages = new StringBuffer(100); 452 | return messages.append(message); 453 | } 454 | 455 | public static String[] readArguments(String workPath, String file) { 456 | BufferedReader br = null; 457 | try { 458 | br = new BufferedReader(new FileReader(new File(workPath, file))); 459 | return Utils.splitStr(br.readLine(), "\""); 460 | } catch (Exception e) { // many can happen 461 | //e.printStackTrace(); 462 | return null; 463 | } finally { 464 | if (br != null) 465 | try { 466 | br.close(); 467 | } catch (IOException ioe) { 468 | } 469 | } 470 | } 471 | 472 | public static void stop() throws IOException { 473 | serve.notifyStop(); 474 | } 475 | 476 | private static void usage() { 477 | System.out.println(Identification.serverName + " " + Identification.serverVersion + "\n" + "Usage: " 478 | + progName + " [-p port] [-s servletpropertiesfile] [-a aliasmappingfile]\n" 479 | + " [-b bind address] [-k backlog] [-l[a][r][f access_log_fmt]]\n" 480 | + " [-c cgi-bin-dir] [-m max_active_session] [-d log_directory]\n" 481 | + " [-sp] [-j jsp_servlet_class] [-w war_deployment_module_class]\n" 482 | + " [-nka] [-kat timeout_in_secs] [-mka max_times_connection_use]\n" 483 | + " [-e [-]duration_in_minutes] [-nohup] [-z max_threadpool_size]\n" 484 | + " [-err [class_name?PrintStream]] [-out [class_name?PrintStream]] [-g ]\n" 485 | + " [-acceptorImpl class_name_of_Accpetor_impl [extra_acceptor_parameters] ]\n" + " Legend:\n" 486 | + " -sp session persistence\n" + " -l access log a - with user agent, and r - referer\n" 487 | + " -nka no keep alive for connection"); 488 | System.exit(1); 489 | } 490 | 491 | private static void killAliveThreads() { 492 | serve.serverThreads.interrupt(); 493 | ThreadGroup tg = Thread.currentThread().getThreadGroup(); 494 | while (tg.getParent() != null) 495 | tg = tg.getParent(); 496 | int ac = tg.activeCount() + tg.activeGroupCount() + 10; 497 | 498 | Thread[] ts = new Thread[ac]; 499 | ac = tg.enumerate(ts, true); 500 | if (ac == ts.length) 501 | serve.log("Destroy:interruptRunningProcesses: Not all threads will be stopped."); 502 | // kill non daemon 503 | for (int i = 0; i < ac; i++) 504 | if (ts[i].isDaemon() == false) { 505 | String tn = ts[i].getName(); 506 | //System.err.println("Interrupting and kill " + tn); 507 | 508 | if (ts[i] == Thread.currentThread() || "Stop Monitor".equals(tn) || "ShutDownHook".equals(tn) 509 | || "DestroyJavaVM".equals(tn) || (tn != null && tn.startsWith("AWT-")) || "main".equals(tn)) 510 | continue; 511 | ts[i].interrupt(); 512 | Thread.yield(); 513 | if (ts[i].isAlive()) { 514 | try { 515 | ts[i].stop(); 516 | } catch (Throwable t) { 517 | if (t instanceof ThreadDeath) { 518 | serve 519 | .log( 520 | "Thread death exception happened and stopping thread, thread stopping loop will be terminated", 521 | t); 522 | throw (ThreadDeath) t; 523 | } else 524 | serve.log("An exception at stopping " + ts[i] + " " + t); 525 | } 526 | } 527 | }// else 528 | //serve.log("Daemon thread "+ts[i].getName()+" is untouched."); 529 | } 530 | 531 | private static void readServlets(File servFile) { 532 | /** 533 | * servlet.properties file format servlet. .code= servlet. .initArgs= , 534 | */ 535 | Hashtable servletstbl, parameterstbl; 536 | servletstbl = new Hashtable(); 537 | parameterstbl = new Hashtable(); 538 | if (servFile != null && servFile.exists() && servFile.canRead()) { 539 | try { 540 | BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(servFile))); 541 | /** 542 | * format of servlet.cfg file servlet_name;servlet_class;init_parameter1=value1;init_parameter2=value2... 543 | */ 544 | do { 545 | String servletdsc = in.readLine(); 546 | if (servletdsc == null) 547 | break; 548 | if (servletdsc.startsWith("#")) 549 | continue; 550 | StringTokenizer dsctokenzr = new StringTokenizer(servletdsc, ".=,", false); 551 | if (dsctokenzr.hasMoreTokens()) { 552 | if (!dsctokenzr.nextToken().equalsIgnoreCase("servlet")) { 553 | serve.log("No leading 'servlet' keyword, the sentence is skipped"); 554 | break; 555 | } 556 | if (dsctokenzr.hasMoreTokens()) { 557 | String servletname = dsctokenzr.nextToken(); 558 | 559 | while (dsctokenzr.hasMoreTokens()) { 560 | String lt = dsctokenzr.nextToken(); 561 | if (lt.equalsIgnoreCase("code")) { 562 | if (dsctokenzr.hasMoreTokens()) 563 | servletstbl.put(servletname, dsctokenzr.nextToken("=")); 564 | } else if (lt.equalsIgnoreCase("initArgs")) { 565 | Hashtable initparams = new Hashtable(); 566 | while (dsctokenzr.hasMoreTokens()) { 567 | String key = dsctokenzr.nextToken("="); 568 | if (key.startsWith(",")) 569 | key = key.substring(1); 570 | //System.err.println("Key:"+key); 571 | try { 572 | initparams.put(key, dsctokenzr.nextToken(",").substring(1).replaceAll("%2c", ",")); 573 | //System.err.println("Key:"+key+" val:"+initparams.get(key)); 574 | } catch(NoSuchElementException nse) { 575 | initparams.put(key,""); 576 | break; 577 | } 578 | } 579 | //System.err.println("init:"+initparams+" for "+servletname); 580 | parameterstbl.put(servletname, initparams); 581 | } else { 582 | servletname +='.'+lt; 583 | serve 584 | .log("No expected token (code|initArgs), "+lt+" added to servlet " 585 | + servletname +" for line: "+servletdsc); 586 | } 587 | } 588 | } 589 | } 590 | } while (true); 591 | } catch (IOException e) { 592 | serve.log("IO problem in processing servlets definition file (" + servFile + "): " + e); 593 | } 594 | Enumeration se = servletstbl.keys(); 595 | String servletname; 596 | while (se.hasMoreElements()) { 597 | servletname = (String) se.nextElement(); 598 | //System.err.println("Adding servlet fro "+servletname+" as "+servletstbl.get(servletname)); 599 | serve.addServlet(servletname, (String) servletstbl.get(servletname), (Hashtable) parameterstbl 600 | .get(servletname)); 601 | } 602 | } else 603 | serve.log("Servlets definition file neither provided, found, nor readable: " + servFile); 604 | } 605 | 606 | static class RollingOutputStream extends FilterOutputStream { 607 | private int rollingThresh; 608 | private File nameBase; 609 | private volatile int currentLine; 610 | private int numRoll; 611 | 612 | public RollingOutputStream(File file, int rollingSize) throws IOException { 613 | super(null); 614 | rollingThresh = rollingSize; 615 | if (rollingThresh < 1000) 616 | rollingThresh = 1000; 617 | nameBase = file; 618 | out = new FileOutputStream(nameBase); 619 | } 620 | 621 | //@Override 1.4 622 | public void flush() throws IOException { 623 | super.flush(); 624 | if (currentLine++ > rollingThresh) { 625 | synchronized (this) { 626 | if (currentLine++ > rollingThresh) { 627 | out.close(); 628 | if (nameBase.renameTo(new File(nameBase.getPath()+ "." + new DecimalFormat("0000").format(numRoll++)))) { 629 | out = new FileOutputStream(nameBase); 630 | currentLine = 0; 631 | } else { 632 | // TODO warn that roll didn't happen - overwriting 633 | out = new FileOutputStream(nameBase); 634 | } 635 | } 636 | } 637 | } 638 | } 639 | } 640 | } 641 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/SSLAcceptor.java: -------------------------------------------------------------------------------- 1 | /* tjws - SSLAcceptor.java 2 | * Copyright (C) 1999-2007 Dmitriy Rogatkin. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 15 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 17 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 18 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 19 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 20 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 21 | * SUCH DAMAGE. 22 | * 23 | * Visit http://tjws.sourceforge.net to get the latest information 24 | * about Rogatkin's products. 25 | * $Id: SSLAcceptor.java,v 1.10 2013/03/02 09:11:56 cvs Exp $ 26 | * Created on Feb 21, 2007 27 | * @author dmitriy 28 | */ 29 | package Acme.Serve; 30 | 31 | import java.io.File; 32 | import java.io.FileInputStream; 33 | import java.io.IOException; 34 | import java.net.InetAddress; 35 | import java.net.ServerSocket; 36 | import java.net.Socket; 37 | import java.security.KeyStore; 38 | import java.security.Security; 39 | import java.util.Map; 40 | 41 | import javax.net.ssl.KeyManagerFactory; 42 | import javax.net.ssl.SSLContext; 43 | import javax.net.ssl.SSLServerSocket; 44 | 45 | import Acme.Utils; 46 | import Acme.Serve.Serve.Acceptor; 47 | 48 | public class SSLAcceptor implements Acceptor { 49 | public static final String ARG_ALGORITHM = "algorithm"; // SUNX509 50 | 51 | public static final String ARG_CLIENTAUTH = "clientAuth"; // false 52 | 53 | public static final String ARG_KEYSTOREFILE = "keystoreFile"; // 54 | 55 | public static final String PROP_KEYSTOREFILE = "javax.net.ssl.keyStore"; // 56 | 57 | public static final String ARG_KEYSTOREPASS = "keystorePass"; // KEYSTOREPASS 58 | 59 | public static final String PROP_KEYSTOREPASS = "javax.net.ssl.keyStorePassword"; 60 | 61 | public static final String ARG_KEYSTORETYPE = "keystoreType"; // KEYSTORETYPE 62 | 63 | public static final String PROP_KEYSTORETYPE = "javax.net.ssl.keyStoreType"; // 64 | 65 | public static final String ARG_KEYPASS = "keyPass"; // 66 | 67 | public static final String ARG_PROTOCOL = "protocol"; // TLS 68 | 69 | public static final String ARG_BACKLOG = Serve.ARG_BACKLOG; 70 | 71 | public static final String ARG_SO_HS_TIMEOUT = "socket-handshake-timeout"; 72 | 73 | public static final String ARG_IFADDRESS = "ifAddress"; 74 | 75 | public static final String ARG_PORT = "ssl-port"; 76 | 77 | public static final String PROTOCOL_HANDLER_JSSE10 = "com.sun.net.ssl.internal.www.protocol"; 78 | 79 | public static final String PROTOCOL_HANDLER = "javax.net.ssl.internal.www.protocol"; 80 | 81 | /** 82 | * The name of the system property containing a "|" delimited list of 83 | * protocol handler packages. 84 | */ 85 | public static final String PROTOCOL_PACKAGES = "java.protocol.handler.pkgs"; 86 | 87 | /** 88 | * Certificate encoding algorithm to be used. 89 | */ 90 | public final static String SUNX509 = "SunX509"; 91 | 92 | /** 93 | * default SSL port 94 | */ 95 | public final static int PORT = 8443; 96 | 97 | /** 98 | * default backlog 99 | */ 100 | public final static int BACKLOG = 1000; 101 | 102 | /** 103 | * Storage type of the key store file to be used. 104 | */ 105 | public final static String KEYSTORETYPE = "JKS"; 106 | 107 | /** 108 | * SSL protocol variant to use. 109 | */ 110 | public final static String TLS = "TLS"; 111 | 112 | /** 113 | * SSL protocol variant to use. 114 | */ 115 | public static final String protocol = TLS; 116 | 117 | /** 118 | * Password for accessing the key store file. 119 | */ 120 | private static final String KEYSTOREPASS = "changeme"; 121 | 122 | /** 123 | * default socket SSL handshake timeout preventing DoS attacks 124 | */ 125 | protected static final int SO_HS_TIMEOIUT = 30 * 1000; 126 | 127 | /** 128 | * Pathname to the key store file to be used. 129 | */ 130 | protected String keystoreFile = System.getProperty("user.home") + File.separator + ".keystore"; 131 | 132 | protected ServerSocket socket; 133 | 134 | protected static int so_hs_timeout; 135 | 136 | protected boolean android, jsse10; 137 | 138 | private String getKeystoreFile() { 139 | return (this.keystoreFile); 140 | } 141 | 142 | public Socket accept() throws IOException { 143 | return socket.accept(); 144 | } 145 | 146 | public void destroy() throws IOException { 147 | try { 148 | socket.close(); 149 | } finally { 150 | socket = null; 151 | } 152 | } 153 | 154 | public void init(Map inProperties, Map outProperties) throws IOException { 155 | // Create the proxy and return 156 | javax.net.ssl.SSLServerSocketFactory sslSoc = initSSLContext(inProperties, outProperties) 157 | .getServerSocketFactory(); 158 | int port = Utils.parseInt(inProperties.get(ARG_PORT), Utils.parseInt(inProperties.get(Serve.ARG_PORT), PORT)); 159 | 160 | if (inProperties.get(ARG_BACKLOG) == null) 161 | if (inProperties.get(ARG_IFADDRESS) == null) 162 | socket = sslSoc.createServerSocket(port); 163 | else 164 | socket = sslSoc.createServerSocket(port, BACKLOG, 165 | InetAddress.getByName((String) inProperties.get(ARG_IFADDRESS))); 166 | else if (inProperties.get(ARG_IFADDRESS) == null) 167 | socket = sslSoc.createServerSocket(port, Utils.parseInt(inProperties.get(ARG_BACKLOG), BACKLOG)); 168 | else 169 | socket = sslSoc.createServerSocket(port, Utils.parseInt(inProperties.get(ARG_BACKLOG), BACKLOG), 170 | InetAddress.getByName((String) inProperties.get(ARG_IFADDRESS))); 171 | 172 | initServerSocket(socket, "true".equals(inProperties.get(ARG_CLIENTAUTH))); 173 | if (outProperties != null) 174 | outProperties.put(Serve.ARG_BINDADDRESS, socket.getInetAddress().getHostName()); 175 | // note it isn't use for the implementation since there is no control over handshake 176 | so_hs_timeout = Utils.parseInt(inProperties.get(ARG_SO_HS_TIMEOUT), SO_HS_TIMEOIUT); 177 | } 178 | 179 | protected SSLContext initSSLContext(Map inProperties, Map outProperties) throws IOException { 180 | // init keystore 181 | KeyStore keyStore = null; 182 | FileInputStream istream = null; 183 | String keystorePass = null; 184 | android = System.getProperty("java.vm.name") != null && System.getProperty("java.vm.name").startsWith("Dalvik"); 185 | try { 186 | String keystoreType = getWithDefault(inProperties, ARG_KEYSTORETYPE, KEYSTORETYPE); 187 | keyStore = KeyStore.getInstance(keystoreType); 188 | String keystoreFile = (String) inProperties.get(ARG_KEYSTOREFILE); 189 | if (keystoreFile == null) 190 | keystoreFile = getKeystoreFile(); 191 | istream = new FileInputStream(keystoreFile); 192 | keystorePass = getWithDefault(inProperties, ARG_KEYSTOREPASS, KEYSTOREPASS); 193 | keyStore.load(istream, keystorePass.toCharArray()); 194 | } catch (Exception e) { 195 | throw (IOException) new IOException(e.toString()).initCause(e); 196 | } finally { 197 | if (istream != null) 198 | istream.close(); 199 | } 200 | 201 | try { 202 | // Register the JSSE security Provider (if it is not already there) 203 | if (android == false) 204 | try { 205 | Security.addProvider((java.security.Provider) Class 206 | .forName("com.sun.net.ssl.internal.ssl.Provider").newInstance()); 207 | } catch (Throwable t) { 208 | if (t instanceof ThreadDeath) 209 | throw (ThreadDeath) t; 210 | // TODO think to do not propagate absence of the provider, since other can still work 211 | throw (IOException) new IOException(t.toString()).initCause(t); 212 | } 213 | 214 | // Create an SSL context used to create an SSL socket factory 215 | String protocol = getWithDefault(inProperties, ARG_PROTOCOL, TLS); 216 | SSLContext context = SSLContext.getInstance(protocol); 217 | 218 | // Create the key manager factory used to extract the server key 219 | String algorithm = getWithDefault(inProperties, ARG_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); 220 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm); 221 | 222 | String keyPass = getWithDefault(inProperties, ARG_KEYPASS, keystorePass); 223 | 224 | keyManagerFactory.init(keyStore, keyPass.toCharArray()); 225 | 226 | // Initialize the context with the key managers 227 | context.init(keyManagerFactory.getKeyManagers(), null, new java.security.SecureRandom()); 228 | return context; 229 | } catch (Exception e) { 230 | System.err.println("SSLsocket creation: " + e); 231 | e.printStackTrace(); 232 | throw (IOException) new IOException(e.toString()).initCause(e); 233 | } 234 | } 235 | 236 | /** 237 | * Register our URLStreamHandler for the "https:" protocol. 238 | */ 239 | protected static void initHandler() { 240 | 241 | String packages = System.getProperty(PROTOCOL_PACKAGES); 242 | if (packages == null) 243 | packages = PROTOCOL_HANDLER; 244 | else if (packages.indexOf(PROTOCOL_HANDLER) < 0) 245 | packages += "|" + PROTOCOL_HANDLER; 246 | System.setProperty(PROTOCOL_PACKAGES, packages); 247 | } 248 | 249 | public String toString() { 250 | return socket != null ? socket.toString() : "SSLAcceptor uninitialized"; 251 | } 252 | 253 | static { 254 | initHandler(); 255 | } 256 | 257 | /** 258 | * Set the requested properties for this server socket. 259 | * 260 | * @param ssocket 261 | * The server socket to be configured 262 | */ 263 | protected void initServerSocket(ServerSocket ssocket, boolean clientAuth) { 264 | 265 | SSLServerSocket socket = (SSLServerSocket) ssocket; 266 | 267 | // Enable all available cipher suites when the socket is connected 268 | String cipherSuites[] = socket.getSupportedCipherSuites(); 269 | socket.setEnabledCipherSuites(cipherSuites); 270 | // Set client authentication if necessary 271 | socket.setNeedClientAuth(clientAuth); 272 | } 273 | 274 | private String getWithDefault(Map args, String name, String defValue) { 275 | String result = (String) args.get(name); 276 | if (result == null) 277 | return defValue; 278 | return result; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/SelectorAcceptor.java: -------------------------------------------------------------------------------- 1 | /* tjws - SelectorAcceptor.java 2 | * Copyright (C) 1999-2007 Dmitriy Rogatkin. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 15 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 17 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 18 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 19 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 20 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 21 | * SUCH DAMAGE. 22 | * 23 | * Visit http://tjws.sourceforge.net to get the latest information 24 | * about Rogatkin's products. 25 | * $Id: SelectorAcceptor.java,v 1.8 2008/01/18 10:05:23 dmitriy Exp $ 26 | * Created on Feb 21, 2007 27 | * @author dmitriy 28 | */ 29 | package Acme.Serve; 30 | 31 | import java.io.IOException; 32 | import java.net.InetSocketAddress; 33 | import java.net.ServerSocket; 34 | import java.net.Socket; 35 | import java.net.InetAddress; 36 | import java.nio.channels.SelectionKey; 37 | import java.nio.channels.Selector; 38 | import java.nio.channels.ServerSocketChannel; 39 | import java.util.Iterator; 40 | import java.util.Map; 41 | 42 | import Acme.Serve.Serve.Acceptor; 43 | 44 | public class SelectorAcceptor implements Acceptor { 45 | private ServerSocketChannel channel; 46 | 47 | private Selector selector; 48 | 49 | private Iterator readyItor; 50 | 51 | public Socket accept() throws IOException { 52 | do { 53 | if (readyItor == null) { 54 | if (selector.select() > 0) 55 | readyItor = selector.selectedKeys().iterator(); 56 | else 57 | throw new IOException(); 58 | } 59 | 60 | if (readyItor.hasNext()) { 61 | 62 | // Get key from set 63 | SelectionKey key = (SelectionKey) readyItor.next(); 64 | 65 | // Remove current entry 66 | readyItor.remove(); 67 | // TODO add processing CancelledKeyException 68 | if (key.isValid() && key.isAcceptable()) { 69 | // Get channel 70 | ServerSocketChannel keyChannel = (ServerSocketChannel) key.channel(); 71 | 72 | // Get server socket 73 | ServerSocket serverSocket = keyChannel.socket(); 74 | 75 | // Accept request 76 | return serverSocket.accept(); 77 | } 78 | } else 79 | readyItor = null; 80 | } while (true); 81 | } 82 | 83 | public void destroy() throws IOException { 84 | String exceptions = ""; 85 | try { 86 | channel.close(); 87 | } catch (IOException e) { 88 | exceptions += e.toString(); 89 | } 90 | try { 91 | selector.close(); 92 | } catch (IOException e) { 93 | exceptions += e.toString(); 94 | } 95 | if (exceptions.length() > 0) 96 | throw new IOException(exceptions); 97 | } 98 | 99 | public void init(Map inProperties, Map outProperties) throws IOException { 100 | selector = Selector.open(); 101 | 102 | channel = ServerSocketChannel.open(); 103 | channel.configureBlocking(false); 104 | int port = inProperties.get(Serve.ARG_PORT) != null ? ((Integer) inProperties.get(Serve.ARG_PORT)).intValue() 105 | : Serve.DEF_PORT; 106 | InetSocketAddress isa = null; 107 | if (inProperties.get(Serve.ARG_BINDADDRESS) != null) 108 | try { 109 | isa = new InetSocketAddress((String) inProperties.get(Serve.ARG_BINDADDRESS), port); 110 | } catch (Exception e) { 111 | } 112 | if (isa == null) 113 | isa = new InetSocketAddress(port); 114 | // TODO add ARG_BACKLOG 115 | channel.socket().bind(isa); 116 | 117 | // Register interest in when connection 118 | channel.register(selector, SelectionKey.OP_ACCEPT); 119 | if (outProperties != null) { 120 | if (channel.socket().isBound()) 121 | outProperties.put(Serve.ARG_BINDADDRESS, channel.socket().getInetAddress().getHostName()); 122 | else 123 | outProperties.put(Serve.ARG_BINDADDRESS, InetAddress.getLocalHost().getHostName()); 124 | } 125 | } 126 | 127 | public String toString() { 128 | return "SelectorAcceptor - "+(channel == null ?"unset":""+channel.socket()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/SimpleAcceptor.java: -------------------------------------------------------------------------------- 1 | /* tjws - SimpleAcceptor.java 2 | * Copyright (C) 1999-2007 Dmitriy Rogatkin. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 15 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 17 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 18 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 19 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 20 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 21 | * SUCH DAMAGE. 22 | * 23 | * Visit http://tjws.sourceforge.net to get the latest information 24 | * about Rogatkin's products. 25 | * $Id: SimpleAcceptor.java,v 1.9 2012/08/16 02:50:15 dmitriy Exp $ 26 | * Created on Jun 12, 2007 27 | * @author dmitriy 28 | */ 29 | package Acme.Serve; 30 | 31 | import java.io.IOException; 32 | import java.net.InetAddress; 33 | import java.net.InetSocketAddress; 34 | import java.net.ServerSocket; 35 | import java.net.Socket; 36 | import java.util.Map; 37 | 38 | public class SimpleAcceptor implements Serve.Acceptor { 39 | public Socket accept() throws IOException { 40 | return socket.accept(); 41 | } 42 | 43 | public void destroy() throws IOException { 44 | if (socket == null) 45 | throw new IOException("Socket already destroyed"); 46 | try { 47 | socket.close(); 48 | } finally { 49 | socket = null; 50 | } 51 | } 52 | 53 | public void init(Map inProperties, Map outProperties) throws IOException { 54 | int port = inProperties.get(Serve.ARG_PORT) != null ? ((Integer) inProperties.get(Serve.ARG_PORT)).intValue() 55 | : Serve.DEF_PORT; 56 | String bindAddrStr = (String) inProperties.get(Serve.ARG_BINDADDRESS); 57 | InetSocketAddress bindAddr = bindAddrStr != null ? new InetSocketAddress(InetAddress.getByName(bindAddrStr), 58 | port) : null; 59 | String backlogStr = (String) inProperties.get(Serve.ARG_BACKLOG); 60 | int backlog = backlogStr != null ? Integer.parseInt(backlogStr) : -1; 61 | if (bindAddr != null) { 62 | socket = new ServerSocket(); 63 | if (backlog < 0) 64 | socket.bind(bindAddr); 65 | else 66 | socket.bind(bindAddr, backlog); 67 | } else { 68 | if (backlog < 0) 69 | socket = new ServerSocket(port); 70 | else 71 | socket = new ServerSocket(port, backlog); 72 | } 73 | if (outProperties != null) 74 | if (socket.isBound()) 75 | outProperties.put(Serve.ARG_BINDADDRESS, socket.getInetAddress().getHostName()); 76 | else 77 | outProperties.put(Serve.ARG_BINDADDRESS, InetAddress.getLocalHost().getHostName()); 78 | } 79 | 80 | public String toString() { 81 | return "SimpleAcceptor " + socket; 82 | } 83 | 84 | private ServerSocket socket; 85 | } -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/ThrottleItem.java: -------------------------------------------------------------------------------- 1 | // ThrottleItem - data item for ThrottledOutputStream 2 | // 3 | // Copyright (C) 1996 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | 29 | package Acme.Serve; 30 | 31 | /// Data item for ThrottledOutputStream. 32 | //

33 | // Fetch the software.
34 | // Fetch the entire Acme package. 35 | 36 | public class ThrottleItem { 37 | 38 | private long maxBps; 39 | 40 | // / Constructor. 41 | public ThrottleItem(long maxBps) { 42 | this.maxBps = maxBps; 43 | } 44 | 45 | public long getMaxBps() { 46 | return maxBps; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/ThrottledOutputStream.java: -------------------------------------------------------------------------------- 1 | // ThrottledOutputStream - output stream with throttling 2 | // 3 | // Copyright (C)1996,1998 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | 29 | package Acme.Serve; 30 | 31 | import java.io.BufferedReader; 32 | import java.io.File; 33 | import java.io.FileReader; 34 | import java.io.FilterOutputStream; 35 | import java.io.IOException; 36 | import java.io.OutputStream; 37 | 38 | /// Output stream with throttling. 39 | //

40 | // Restricts output to a specified rate. Also includes a static utility 41 | // routine for parsing a file of throttle settings. 42 | //

43 | // Fetch the software.
44 | // Fetch the entire Acme package. 45 | 46 | public class ThrottledOutputStream extends FilterOutputStream { 47 | 48 | // / Parses a standard throttle file. 49 | //

50 | // A throttle file lets you set maximum byte rates on filename patterns. 51 | // The format of the throttle file is very simple. A # starts a 52 | // comment, and the rest of the line is ignored. Blank lines are ignored. 53 | // The rest of the lines should consist of a pattern, whitespace, and a 54 | // number. The pattern is a simple shell-style filename pattern, using 55 | // ? and *, or multiple such patterns separated by |. 56 | //

57 | // The numbers in the file are byte rates, specified in units of bytes 58 | // per second. For comparison, a v.32b/v.42b modem gives about 59 | // 1500/2000 B/s depending on compression, a double-B-channel ISDN line 60 | // about 12800 B/s, and a T1 line is about 150000 B/s. 61 | //

62 | // Example: 63 | //

 64 | 	// # throttle file for www.acme.com
 65 | 	// * 100000 # limit total web usage to 2/3 of our T1
 66 | 	// *.jpg|*.gif 50000 # limit images to 1/3 of our T1
 67 | 	// *.mpg 20000 # and movies to even less
 68 | 	// jef/* 20000 # jef's pages are too popular
 69 | 	// 
70 | //

71 | // The routine returns a WildcardDictionary. Do a lookup in this 72 | // dictionary using a filename, and you'll get back a ThrottleItem 73 | // containing the corresponding byte rate. 74 | public static Acme.WildcardDictionary parseThrottleFile(String filename) throws IOException { 75 | Acme.WildcardDictionary wcd = new Acme.WildcardDictionary(); 76 | File thFile = new File(filename); 77 | if (thFile.isAbsolute() == false) 78 | thFile = new File(System.getProperty("user.dir", "."), thFile.getName()); 79 | BufferedReader br = new BufferedReader(new FileReader(thFile)); 80 | while (true) { 81 | String line = br.readLine(); 82 | if (line == null) 83 | break; 84 | int i = line.indexOf('#'); 85 | if (i != -1) 86 | line = line.substring(0, i); 87 | line = line.trim(); 88 | if (line.length() == 0) 89 | continue; 90 | String[] words = Acme.Utils.splitStr(line); 91 | if (words.length != 2) 92 | throw new IOException("malformed throttle line: " + line); 93 | try { 94 | wcd.put(words[0], new ThrottleItem(Long.parseLong(words[1]))); 95 | } catch (NumberFormatException e) { 96 | throw new IOException("malformed number in throttle line: " + line); 97 | } 98 | } 99 | br.close(); 100 | return wcd; 101 | } 102 | 103 | private long maxBps; 104 | 105 | private long bytes; 106 | 107 | private long start; 108 | 109 | // / Constructor. 110 | public ThrottledOutputStream(OutputStream out, long maxBps) { 111 | super(out); 112 | this.maxBps = maxBps; 113 | bytes = 0; 114 | start = System.currentTimeMillis(); 115 | } 116 | 117 | private byte[] oneByte = new byte[1]; 118 | 119 | // / Writes a byte. This method will block until the byte is actually 120 | // written. 121 | // @param b the byte to be written 122 | // @exception IOException if an I/O error has occurred 123 | public void write(int b) throws IOException { 124 | oneByte[0] = (byte) b; 125 | write(oneByte, 0, 1); 126 | } 127 | 128 | // / Writes a subarray of bytes. 129 | // @param b the data to be written 130 | // @param off the start offset in the data 131 | // @param len the number of bytes that are written 132 | // @exception IOException if an I/O error has occurred 133 | public void write(byte b[], int off, int len) throws IOException { 134 | // Check the throttle. 135 | bytes += len; 136 | long elapsed = Math.max(System.currentTimeMillis() - start, 1); 137 | 138 | long bps = bytes * 1000L / elapsed; 139 | if (bps > maxBps) { 140 | // Oops, sending too fast. 141 | long wakeElapsed = bytes * 1000L / maxBps; 142 | try { 143 | Thread.sleep(wakeElapsed - elapsed); 144 | } catch (InterruptedException ignore) { 145 | } 146 | } 147 | 148 | // Write the bytes. 149 | out.write(b, off, len); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Serve/WarDeployer.java: -------------------------------------------------------------------------------- 1 | /* tjws - WarDeployer.java 2 | * Copyright (C) 1999-2004 Dmitriy Rogatkin. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 15 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 16 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 17 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 18 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 19 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 20 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | * 22 | * $Id: WarDeployer.java,v 1.1 2006/05/10 05:45:21 rogatkin Exp $ 23 | * Created on Dec 13, 2004 24 | */ 25 | 26 | package Acme.Serve; 27 | 28 | /** 29 | * @author dmitriy 30 | * 31 | * 32 | */ 33 | public interface WarDeployer { 34 | void deploy(Serve server); 35 | } 36 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/Utils.java: -------------------------------------------------------------------------------- 1 | // Utils - assorted static utility routines 2 | // 3 | // Copyright (C)1996,1998 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | // 29 | // Base64 code borrowed from public domain supported by Robert Harder 30 | // Please visit http://iharder.net/base64 31 | // periodically to check for updates or to contribute improvements. 32 | // 33 | // All enhancements Copyright (C)1998-2010 by Dmitriy Rogatkin 34 | // 35 | // $Id: Utils.java,v 1.39 2013/08/10 02:47:26 cvs Exp $ 36 | 37 | package Acme; 38 | 39 | import java.io.File; 40 | import java.io.IOException; 41 | import java.io.InputStream; 42 | import java.io.OutputStream; 43 | import java.io.PrintStream; 44 | import java.io.Reader; 45 | import java.io.UnsupportedEncodingException; 46 | import java.io.Writer; 47 | import java.net.URL; 48 | import java.net.URLClassLoader; 49 | import java.text.SimpleDateFormat; 50 | import java.util.ArrayList; 51 | import java.util.Date; 52 | import java.util.Enumeration; 53 | import java.util.HashMap; 54 | import java.util.Hashtable; 55 | import java.util.Iterator; 56 | import java.util.List; 57 | import java.util.Map; 58 | import java.util.Properties; 59 | import java.util.StringTokenizer; 60 | 61 | /// Assorted static utility routines. 62 | //

63 | // Whenever I come up with a static routine that might be of general use, 64 | // I put it here. So far the class includes: 65 | //

    66 | //
  • some string routines that were left out of java.lang.String 67 | //
  • a general array-to-string routine 68 | //
  • a fixed version of java.io.InputStream's byte-array read routine 69 | //
  • a bunch of URL-hacking routines 70 | //
  • some easy-to-use wrappers for Runtime.exec 71 | //
  • a debugging routine to dump the current call stack 72 | //
  • a URLDecoder to match java.net.URLEncoder 73 | //
74 | // and lots more. 75 | //

76 | // Fetch the software.
77 | // Fetch the entire Acme package. 78 | 79 | public class Utils { 80 | // / Returns a date string formatted in Unix ls style - if it's within 81 | // six months of now, Mmm dd hh:ss, else Mmm dd yyyy. 82 | static final SimpleDateFormat shortfmt = new SimpleDateFormat("MMM dd HH:mm"); 83 | 84 | static final SimpleDateFormat longfmt = new SimpleDateFormat("MMM dd yyyy"); 85 | 86 | public static final int COPY_BUF_SIZE = 4096 * 2; 87 | 88 | public final static String ISO_8859_1 = "ISO-8859-1"; 89 | 90 | public static final Class[] EMPTY_CLASSES = {}; 91 | 92 | public static final Object[] EMPTY_OBJECTS = {}; 93 | 94 | public static final Enumeration EMPTY_ENUMERATION = new Enumeration() { 95 | public boolean hasMoreElements() { 96 | return false; 97 | } 98 | 99 | public Object nextElement() { 100 | return null; 101 | } 102 | }; 103 | 104 | public static String lsDateStr(Date date) { 105 | if (Math.abs(System.currentTimeMillis() - date.getTime()) < 183L * 24L * 60L * 60L * 1000L) 106 | return shortfmt.format(date); 107 | else 108 | return longfmt.format(date); 109 | } 110 | 111 | public static Hashtable parseQueryString(String query, String encoding) { 112 | Hashtable result = new Hashtable(); 113 | if (encoding == null) 114 | encoding = "UTF-8"; 115 | StringTokenizer st = new StringTokenizer(query, "&"); 116 | while (st.hasMoreTokens()) { 117 | String pair = st.nextToken(); 118 | int ep = pair.indexOf('='); 119 | String key = ep > 0 ? pair.substring(0, ep) : pair; 120 | String value = ep > 0 ? pair.substring(ep + 1) : ""; 121 | try { 122 | key = /* URLDecoder. */decode(key, encoding); 123 | if (value != null) 124 | value = /* URLDecoder. */decode(value, encoding); 125 | } catch (UnsupportedEncodingException uee) { 126 | } 127 | String[] values = (String[]) result.get(key); 128 | String[] newValues; 129 | if (values == null) { 130 | newValues = new String[1]; 131 | newValues[0] = value; 132 | } else { 133 | newValues = new String[values.length + 1]; 134 | System.arraycopy(values, 0, newValues, 0, values.length); 135 | newValues[values.length] = value; 136 | } 137 | result.put(key, newValues); 138 | } 139 | return result; 140 | } 141 | 142 | public static Map parsePostData(long len, InputStream is, String encoding, String[] cachedStream) 143 | throws IOException { 144 | // TODO: handle parsing data over 2 GB 145 | if (len > Integer.MAX_VALUE) 146 | throw new RuntimeException("Can't process POST data over " + Integer.MAX_VALUE + ", requested: " + len); 147 | byte[] buf = new byte[(int) len]; 148 | int fp = 0; 149 | while (fp < len) { 150 | int c = is.read(buf, fp, buf.length - fp); 151 | if (c < 0) 152 | break; 153 | fp += c; 154 | } 155 | //System.err.println("====>"+new String( buf)); 156 | if (cachedStream != null && cachedStream.length > 0) 157 | return parseQueryString(cachedStream[0] = new String(buf, 0, fp, ISO_8859_1), encoding); 158 | else 159 | return parseQueryString(new String(buf, 0, fp, ISO_8859_1), encoding); 160 | } 161 | 162 | /** 163 | * Decodes URL encoded string including newly introduced JavaScript encoding with %uxxxx chars 164 | * 165 | * @param s 166 | * encoded string 167 | * @param enc 168 | * source encoding 169 | * @return decoded string or original if no decoding required 170 | * @throws UnsupportedEncodingException 171 | */ 172 | public static String decode(String s, String enc) throws UnsupportedEncodingException { 173 | if (enc == null || enc.length() == 0) { 174 | throw new UnsupportedEncodingException("decode: no source char encoding provided."); 175 | } 176 | if (s == null) 177 | return null; 178 | boolean decoded = false; 179 | int l = s.length(); 180 | StringBuffer sb = new StringBuffer(l > 1024 ? l / 3 : l); 181 | 182 | int state = sText; 183 | int i = 0; 184 | int code = 0; 185 | char c; 186 | int pos = 0; 187 | int ofs = 0; 188 | byte[] buf = null; 189 | boolean processDig = false; 190 | while (i < l) { 191 | c = s.charAt(i); 192 | switch (c) { 193 | case '+': 194 | decoded = true; 195 | if (state == sText) 196 | sb.append(' '); 197 | else if (state == s2Dig) { 198 | sb.append(new String(buf, 0, pos + 1, enc)); 199 | state = sText; 200 | sb.append(' '); 201 | } else 202 | new IllegalArgumentException("decode: unexpected + at pos: " + i + ", of : " + s); 203 | break; 204 | case '0': 205 | case '1': 206 | case '2': 207 | case '3': 208 | case '4': 209 | case '5': 210 | case '6': 211 | case '7': 212 | case '8': 213 | case '9': 214 | ofs = '0'; 215 | processDig = true; 216 | break; 217 | case 'a': 218 | case 'b': 219 | case 'c': 220 | case 'd': 221 | case 'e': 222 | case 'f': 223 | ofs = 'a' - 10; 224 | processDig = true; 225 | break; 226 | case 'A': 227 | case 'B': 228 | case 'C': 229 | case 'D': 230 | case 'E': 231 | case 'F': 232 | ofs = 'A' - 10; 233 | processDig = true; 234 | break; 235 | case '%': 236 | decoded = true; 237 | if (state == sText) { 238 | state = sEscape; 239 | if (buf == null) 240 | buf = new byte[(l - i) / 3]; 241 | pos = 0; 242 | } else if (state == s2Dig) { 243 | state = sEscape; 244 | pos++; 245 | } else 246 | new IllegalArgumentException("decode: unexpected escape % at pos: " + i + ", of : " + s); 247 | break; 248 | case 'u': 249 | if (state == sEscape) { 250 | if (pos > 0) { 251 | sb.append(new String(buf, 0, pos, enc)); 252 | pos = 0; 253 | } 254 | state = sU1; 255 | } else if (state == sText) { 256 | sb.append(c); 257 | } else if (state == s2Dig) { 258 | sb.append(new String(buf, 0, pos + 1, enc)); 259 | state = sText; 260 | sb.append(c); 261 | } else 262 | new IllegalArgumentException("decode: unexpected char in hex at pos: " + i + ", of : " + s); 263 | break; 264 | default: 265 | if (state == sText) 266 | sb.append(c); 267 | else if (state == s2Dig) { 268 | sb.append(new String(buf, 0, pos + 1, enc)); 269 | state = sText; 270 | sb.append(c); 271 | } else 272 | new IllegalArgumentException("decode: unexpected char in hex at pos: " + i + ", of : " + s); 273 | 274 | break; 275 | } 276 | i++; 277 | if (processDig) { 278 | if (state == sEscape) { 279 | code = c - ofs; 280 | state = s1Dig; 281 | } else if (state == s1Dig) { 282 | buf[pos] = (byte) (code * 16 + (c - ofs)); 283 | state = s2Dig; 284 | } else if (state == s2Dig) { // escape finished 285 | sb.append(new String(buf, 0, pos + 1, enc)); 286 | state = sText; 287 | sb.append(c); 288 | } else if (state == sU1) { 289 | code = c - ofs; 290 | state = sU2; 291 | } else if (state == sU2) { 292 | code = code * 16 + c - ofs; 293 | state = sU3; 294 | } else if (state == sU3) { 295 | code = code * 16 + c - ofs; 296 | state = sU4; 297 | } else if (state == sU4) { 298 | sb.append((char) (code * 16 + c - ofs)); 299 | state = sText; 300 | } else 301 | sb.append(c); 302 | processDig = false; 303 | } 304 | } 305 | if (state == s2Dig) 306 | sb.append(new String(buf, 0, pos + 1, enc)); 307 | return (decoded ? sb.toString() : s); 308 | } 309 | 310 | private static final int sText = 0; 311 | 312 | private static final int s1Dig = 1; 313 | 314 | private static final int s2Dig = 2; 315 | 316 | private static final int sEscape = 3; 317 | 318 | private static final int sU1 = 4; 319 | 320 | private static final int sU2 = 5; 321 | 322 | private static final int sU3 = 6; 323 | 324 | private static final int sU4 = 7; 325 | 326 | public static String htmlEncode(String s, boolean encodeWS) { 327 | if (s == null) 328 | return null; 329 | char[] ca = s.toCharArray(); 330 | StringBuffer res = new StringBuffer(ca.length); 331 | int ls = 0; 332 | boolean blankMet = true; 333 | for (int i = 0; i < ca.length; i++) { 334 | switch (ca[i]) { 335 | case '<': 336 | res.append(ca, ls, i - ls); 337 | res.append("<"); 338 | ls = i + 1; 339 | break; 340 | case '>': 341 | res.append(ca, ls, i - ls); 342 | res.append(">"); 343 | ls = i + 1; 344 | break; 345 | case '"': 346 | res.append(ca, ls, i - ls); 347 | res.append("""); 348 | ls = i + 1; 349 | break; 350 | case '&': 351 | res.append(ca, ls, i - ls); 352 | res.append("&"); 353 | ls = i + 1; 354 | break; 355 | case ' ': 356 | if (blankMet && encodeWS) { 357 | res.append(ca, ls, i - ls); 358 | res.append(" "); 359 | ls = i + 1; 360 | } else 361 | blankMet = true; 362 | break; 363 | case '\n': 364 | if (encodeWS) { 365 | res.append(ca, ls, i - ls); 366 | res.append("
"); 367 | ls = i + 1; 368 | } 369 | break; 370 | case '\r': 371 | if (encodeWS) { 372 | res.append(ca, ls, i - ls); 373 | ls = i + 1; 374 | } 375 | break; 376 | default: 377 | if (ca[i] > 127) { // no unicode 378 | res.append(ca, ls, i - ls); 379 | res.append("&#").append((int)ca[i]).append(';'); 380 | ls = i + 1; 381 | } 382 | blankMet = false; 383 | } 384 | } 385 | if (ls < ca.length) 386 | res.append(ca, ls, ca.length - ls); 387 | return res.toString(); 388 | } 389 | 390 | public static float isGzipAccepted(String contentEncoding) { 391 | float result = 0f; 392 | if (contentEncoding != null) { 393 | int gzsl = "gzip;".length(); 394 | int zp = contentEncoding.indexOf("gzip"); 395 | if (zp >= 0) { 396 | if (contentEncoding.length() > (zp+gzsl) &&contentEncoding.charAt(zp + gzsl) == ';') { 397 | zp = contentEncoding.indexOf("q=", zp + gzsl); 398 | if (zp > 0) { 399 | int qe = contentEncoding.indexOf(",", zp); 400 | if (qe < 0) 401 | qe = contentEncoding.length(); 402 | try { 403 | result = Float.parseFloat(contentEncoding.substring(zp + 2, qe)); 404 | } catch (NumberFormatException e) { 405 | } 406 | } 407 | } else 408 | result = 1f; 409 | } 410 | } 411 | return result; 412 | } 413 | 414 | // / Checks whether a string matches a given wildcard pattern. 415 | // Only does ? and *, and multiple patterns separated by |. 416 | public static boolean match(String pattern, String string) { 417 | for (int p = 0;; ++p) { 418 | for (int s = 0;; ++p, ++s) { 419 | boolean sEnd = (s >= string.length()); 420 | boolean pEnd = (p >= pattern.length() || pattern.charAt(p) == '|'); 421 | if (sEnd && pEnd) 422 | return true; 423 | if (sEnd || pEnd) 424 | break; 425 | if (pattern.charAt(p) == '?') 426 | continue; 427 | if (pattern.charAt(p) == '*') { 428 | int i; 429 | ++p; 430 | for (i = string.length(); i >= s; --i) 431 | if (match(pattern.substring(p), string.substring(i))) // not quite right 432 | return true; 433 | break; 434 | } 435 | if (pattern.charAt(p) != string.charAt(s)) 436 | break; 437 | } 438 | p = pattern.indexOf('|', p); 439 | if (p == -1) 440 | return false; 441 | } 442 | } 443 | 444 | // / Finds the maximum length of a string that matches a given wildcard 445 | // pattern. Only does ? and *, and multiple patterns separated by |. 446 | public static int matchSpan(String pattern, String string) { 447 | int result = 0; 448 | StringTokenizer st = new StringTokenizer(pattern, "|"); 449 | 450 | while (st.hasMoreTokens()) { 451 | int len = matchSpan1(st.nextToken(), string); 452 | if (len > result) 453 | result = len; 454 | } 455 | return result; 456 | } 457 | 458 | static int matchSpan1(String pattern, String string) { 459 | int p = 0; 460 | for (; p < string.length() && p < pattern.length(); p++) { 461 | if (pattern.charAt(p) == string.charAt(p)) 462 | continue; 463 | if (pattern.charAt(p) == '*') 464 | return p - 1; 465 | return 0; 466 | } 467 | return p < (pattern.length() - 1) ? -1 : p; 468 | } 469 | 470 | // / Turns a String into an array of Strings, by using StringTokenizer 471 | // to split it up at whitespace. 472 | public static String[] splitStr(String str) { 473 | StringTokenizer st = new StringTokenizer(str); 474 | int n = st.countTokens(); 475 | String[] strs = new String[n]; 476 | for (int i = 0; i < n; ++i) 477 | strs[i] = st.nextToken(); 478 | return strs; 479 | } 480 | 481 | // / Turns a String into an array of Strings, by splitting it at 482 | // the specified character. This does not use StringTokenizer, 483 | // and therefore can handle empty fields. 484 | public static String[] splitStr(String str, char delim) { 485 | int n = 1; 486 | int index = -1; 487 | while (true) { 488 | index = str.indexOf(delim, index + 1); 489 | if (index == -1) 490 | break; 491 | ++n; 492 | } 493 | String[] strs = new String[n]; 494 | index = -1; 495 | for (int i = 0; i < n - 1; ++i) { 496 | int nextIndex = str.indexOf(delim, index + 1); 497 | strs[i] = str.substring(index + 1, nextIndex); 498 | index = nextIndex; 499 | } 500 | strs[n - 1] = str.substring(index + 1); 501 | return strs; 502 | } 503 | 504 | public static String[] splitStr(String str, String quotes) { 505 | char[] ca = str.toCharArray(); 506 | // List result = new ArrayList(10); 507 | String[] result = new String[0]; 508 | boolean inArg = false; 509 | boolean quoted = false; 510 | int argStart = -1; 511 | for (int i = 0; i < ca.length; i++) { 512 | char c = ca[i]; 513 | if (inArg) { 514 | if (quoted) { 515 | if (quotes.indexOf(c) >= 0) { 516 | result = copyOf(result, result.length + 1); 517 | result[result.length - 1] = new String(ca, argStart, i - argStart); 518 | argStart = -1; 519 | quoted = false; 520 | inArg = false; 521 | } 522 | } else { 523 | if (c == ' ') { 524 | result = copyOf(result, result.length + 1); 525 | result[result.length - 1] = new String(ca, argStart, i - argStart); 526 | argStart = -1; 527 | inArg = false; 528 | } 529 | } 530 | } else { 531 | if (c != ' ') { 532 | inArg = true; 533 | if (quotes.indexOf(c) >= 0) { 534 | quoted = true; 535 | argStart = i + 1; 536 | } else 537 | argStart = i; 538 | } 539 | } 540 | } 541 | if (argStart > 0) { 542 | result = copyOf(result, result.length + 1); 543 | result[result.length - 1] = new String(ca, argStart, ca.length - argStart); 544 | } 545 | // for(int i=0;i 0) 589 | pathElems.remove(pathElems.size() - 1); 590 | else 591 | lev--; 592 | // else exception ? 593 | } else if (el.equals(".") == false) 594 | if (lev >= 0) 595 | pathElems.add(el); 596 | else 597 | lev++; 598 | if (f) { 599 | s = i; 600 | break; 601 | } 602 | s = -1; 603 | } 604 | } 605 | } 606 | if (s > 0) { 607 | String el = new String(pa, s, n - s); 608 | if (el.equals("..")) { 609 | if (pathElems.size() > 0) 610 | pathElems.remove(pathElems.size() - 1); 611 | // else exception ? 612 | } else if (el.equals(".") == false) 613 | if (lev >= 0) 614 | pathElems.add(el); 615 | } else 616 | pathElems.add(""); 617 | if (pathElems.size() == 0) 618 | return lev>=0?"":null; 619 | StringBuffer result = new StringBuffer(n); 620 | result.append(pathElems.get(0)); 621 | n = pathElems.size(); 622 | for (int i = 1; i < n; i++) 623 | result.append('/').append(pathElems.get(i)); 624 | // System.err.println("Before "+path+" after "+result); 625 | return result.toString(); 626 | } 627 | 628 | // / Copy the input to the output until EOF. 629 | public static long copyStream(InputStream in, OutputStream out, long maxLen) throws IOException { 630 | byte[] buf = new byte[COPY_BUF_SIZE]; 631 | int len; 632 | long tot = 0; 633 | if (maxLen <= 0) 634 | while ((len = in.read(buf)) > 0) { 635 | out.write(buf, 0, len); 636 | tot += len; 637 | } 638 | else 639 | while ((len = in.read(buf)) > 0) 640 | if (len <= maxLen) { 641 | out.write(buf, 0, len); 642 | maxLen -= len; 643 | tot += len; 644 | } else { 645 | out.write(buf, 0, (int) maxLen); 646 | tot += maxLen; 647 | break; 648 | } 649 | return tot; 650 | } 651 | 652 | // / Copy the input to the output until EOF. 653 | public static void copyStream(Reader in, Writer out) throws IOException { 654 | char[] buf = new char[COPY_BUF_SIZE]; 655 | int len; 656 | while ((len = in.read(buf)) != -1) 657 | out.write(buf, 0, len); 658 | } 659 | 660 | // / Copy the input to the output until EOF. 661 | public static void copyStream(Reader in, OutputStream out, String charSet) throws IOException { 662 | char[] buf = new char[4096]; 663 | int len; 664 | if (charSet == null) 665 | while ((len = in.read(buf)) != -1) { 666 | out.write(new String(buf, 0, len).getBytes()); 667 | } 668 | else 669 | while ((len = in.read(buf)) != -1) 670 | out.write(new String(buf, 0, len).getBytes(charSet)); 671 | } 672 | 673 | protected final static char BASE64ARRAY[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 674 | 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 675 | 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', 676 | '4', '5', '6', '7', '8', '9', '+', '/' }; 677 | 678 | /** 679 | * base 64 encoding, string converted to bytes using specified encoding 680 | * 681 | * @param String 682 | * _s original string to encode 683 | * @param String 684 | * encoding, can be null, then iso-8859-1 used 685 | * @return String result of encoding as iso-8859-1 string
686 | * return null in case of invalid encoding or original string null 687 | * @exception no 688 | * exceptions 689 | */ 690 | public final static String base64Encode(String _s, String _enc) { 691 | if (_s == null) 692 | return null; 693 | if (_enc == null) 694 | _enc = ISO_8859_1; 695 | try { 696 | return base64Encode(_s.getBytes(_enc)); 697 | } catch (Exception e) { 698 | e.printStackTrace(); 699 | } 700 | return null; 701 | } 702 | 703 | /** 704 | * base 64 encoding, array of bytes converted to bytes using specified encoding 705 | * 706 | * @param String 707 | * _s original string to encode 708 | * @param String 709 | * encoding, can be null, then iso-8859-1 used 710 | * @return String result of encoding as iso-8859-1 string
711 | * 712 | * @exception NullPointerException if input parameter is null 713 | */ 714 | public final static String base64Encode(byte[] _bytes) { 715 | StringBuffer encodedBuffer = new StringBuffer((int) (_bytes.length * 1.5)); 716 | int i = 0; 717 | int pad = 0; 718 | while (i < _bytes.length) { 719 | int b1 = (0xFF & _bytes[i++]); 720 | int b2; 721 | int b3; 722 | if (i >= _bytes.length) { 723 | b2 = 0; 724 | b3 = 0; 725 | pad = 2; 726 | } else { 727 | b2 = 0xFF & _bytes[i++]; 728 | if (i >= _bytes.length) { 729 | b3 = 0; 730 | pad = 1; 731 | } else 732 | b3 = (0xFF & _bytes[i++]); 733 | } 734 | byte c1 = (byte) (b1 >> 2); 735 | byte c2 = (byte) (((b1 & 0x3) << 4) | (b2 >> 4)); 736 | byte c3 = (byte) (((b2 & 0xf) << 2) | (b3 >> 6)); 737 | byte c4 = (byte) (b3 & 0x3f); 738 | encodedBuffer.append(BASE64ARRAY[c1]).append(BASE64ARRAY[c2]); 739 | switch (pad) { 740 | case 0: 741 | encodedBuffer.append(BASE64ARRAY[c3]).append(BASE64ARRAY[c4]); 742 | break; 743 | case 1: 744 | encodedBuffer.append(BASE64ARRAY[c3]).append('='); 745 | break; 746 | case 2: 747 | encodedBuffer.append("=="); 748 | break; 749 | } 750 | } 751 | return encodedBuffer.toString(); 752 | } 753 | 754 | /** 755 | * Translates a Base64 value to either its 6-bit reconstruction value or a negative number indicating some other meaning. 756 | */ 757 | protected final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 758 | -5, -5, // Whitespace: Tab and Linefeed 759 | -9, -9, // Decimal 11 - 12 760 | -5, // Whitespace: Carriage Return 761 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 762 | // 26 763 | -9, -9, -9, -9, -9, // Decimal 27 - 31 764 | -5, // Whitespace: Space 765 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 766 | 62, // Plus sign at decimal 43 767 | -9, -9, -9, // Decimal 44 - 46 768 | 63, // Slash at decimal 47 769 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine 770 | -9, -9, -9, // Decimal 58 - 60 771 | -1, // Equals sign at decimal 61 772 | -9, -9, -9, // Decimal 62 - 64 773 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' 774 | // through 'N' 775 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' 776 | // through 'Z' 777 | -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 778 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' 779 | // through 'm' 780 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' 781 | // through 'z' 782 | -9, -9, -9, -9 // Decimal 123 - 126 783 | }; 784 | 785 | protected final static byte WHITE_SPACE_ENC = -5; // Indicates white space 786 | 787 | // in encoding 788 | 789 | protected final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign 790 | 791 | // in encoding 792 | 793 | /** The equals sign (=) as a byte. */ 794 | protected final static byte EQUALS_SIGN = (byte) '='; 795 | 796 | /** 797 | * base 64 decoding 798 | * 799 | * @param encoded 800 | * string 801 | * @param encoding 802 | * used to get string bytes 803 | * @return result of encoding, or null if encoding invalid or string null, or string is invalid base 64 encoding 804 | */ 805 | public final static String base64Decode(String _s, String _enc) { 806 | if (_s == null) 807 | return null; 808 | if (_enc == null) 809 | _enc = ISO_8859_1; 810 | try { 811 | return new String(decode64(_s), _enc); 812 | } catch (UnsupportedEncodingException uee) { 813 | } 814 | return null; 815 | } 816 | 817 | /** 818 | * Decodes four bytes from array source and writes the resulting bytes (up to three of them) to destination. The source and 819 | * destination arrays can be manipulated anywhere along their length by specifying srcOffset and destOffset. This method does not 820 | * check to make sure your arrays are large enough to accomodate srcOffset + 4 for the source array or destOffset + 3 821 | * for the destination array. This method returns the actual number of bytes that were converted from the Base64 encoding. 822 | * 823 | * 824 | * @param source 825 | * the array to convert 826 | * @param srcOffset 827 | * the index where conversion begins 828 | * @param destination 829 | * the array to hold the conversion 830 | * @param destOffset 831 | * the index where output will be put 832 | * @return the number of decoded bytes converted 833 | * @since 1.3 834 | */ 835 | private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) { 836 | // Example: Dk== 837 | if (source[srcOffset + 2] == EQUALS_SIGN) { 838 | // Two ways to do the same thing. Don't know which way I like best. 839 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 840 | // ) 841 | // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 842 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 843 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); 844 | 845 | destination[destOffset] = (byte) (outBuff >>> 16); 846 | return 1; 847 | } 848 | 849 | // Example: DkL= 850 | else if (source[srcOffset + 3] == EQUALS_SIGN) { 851 | // Two ways to do the same thing. Don't know which way I like best. 852 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 853 | // ) 854 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 855 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 856 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 857 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) 858 | | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); 859 | 860 | destination[destOffset] = (byte) (outBuff >>> 16); 861 | destination[destOffset + 1] = (byte) (outBuff >>> 8); 862 | return 2; 863 | } 864 | 865 | // Example: DkLE 866 | else { 867 | try { 868 | // Two ways to do the same thing. Don't know which way I like 869 | // best. 870 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) 871 | // >>> 6 ) 872 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 873 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 874 | // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 875 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 876 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) 877 | | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) 878 | | ((DECODABET[source[srcOffset + 3]] & 0xFF)); 879 | 880 | destination[destOffset] = (byte) (outBuff >> 16); 881 | destination[destOffset + 1] = (byte) (outBuff >> 8); 882 | destination[destOffset + 2] = (byte) (outBuff); 883 | 884 | return 3; 885 | } catch (Exception e) { 886 | System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]])); 887 | System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]])); 888 | System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]])); 889 | System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]])); 890 | return -1; 891 | } // e nd catch 892 | } 893 | } // end decodeToBytes 894 | 895 | /** 896 | * Very low-level access to decoding ASCII characters in the form of a byte array. Does not support automatically gunzipping or any other "fancy" features. 897 | * 898 | * @param source 899 | * The Base64 encoded data 900 | * @param off 901 | * The offset of where to begin decoding 902 | * @param len 903 | * The length of characters to decode 904 | * @return decoded data 905 | * @since 1.3 906 | */ 907 | public static byte[] decode(byte[] source, int off, int len) { 908 | int len34 = len * 3 / 4; 909 | byte[] outBuff = new byte[len34]; // Upper limit on size of output 910 | int outBuffPosn = 0; 911 | 912 | byte[] b4 = new byte[4]; 913 | int b4Posn = 0; 914 | int i = 0; 915 | byte sbiCrop = 0; 916 | byte sbiDecode = 0; 917 | for (i = off; i < off + len; i++) { 918 | sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits 919 | sbiDecode = DECODABET[sbiCrop]; 920 | 921 | if (sbiDecode >= WHITE_SPACE_ENC) // Whitesp ace,Eq ualssi gnor be 922 | // tter 923 | { 924 | if (sbiDecode >= EQUALS_SIGN_ENC) { 925 | b4[b4Posn++] = sbiCrop; 926 | if (b4Posn > 3) { 927 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn); 928 | b4Posn = 0; 929 | 930 | // If that was the equals sign, break out of 'for' loop 931 | if (sbiCrop == EQUALS_SIGN) 932 | break; 933 | } // end if: quartet built 934 | 935 | } // end if: equals sign or better 936 | 937 | } // end if: white space, equals sign or better 938 | else { 939 | System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); 940 | return null; 941 | } // end else: 942 | } // each input character 943 | 944 | byte[] out = new byte[outBuffPosn]; 945 | System.arraycopy(outBuff, 0, out, 0, outBuffPosn); 946 | return out; 947 | } // end decode 948 | 949 | /** 950 | * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it. 951 | * 952 | * @param s 953 | * the string to decode 954 | * @return the decoded data 955 | * @since 1.4 956 | */ 957 | public static byte[] decode64(String s) { 958 | byte[] bytes; 959 | try { 960 | bytes = s.getBytes(ISO_8859_1); 961 | } // end try 962 | catch (java.io.UnsupportedEncodingException uee) { 963 | bytes = s.getBytes(); 964 | } // end catch 965 | // 966 | 967 | // Decode 968 | bytes = decode(bytes, 0, bytes.length); 969 | 970 | // Check to see if it's gzip-compressed 971 | // GZIP Magic Two-Byte Number: 0x8b1f (35615) 972 | if (bytes != null && bytes.length >= 4) { 973 | 974 | int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); 975 | if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { 976 | java.io.ByteArrayInputStream bais = null; 977 | java.util.zip.GZIPInputStream gzis = null; 978 | java.io.ByteArrayOutputStream baos = null; 979 | byte[] buffer = new byte[2048]; 980 | int length = 0; 981 | 982 | try { 983 | baos = new java.io.ByteArrayOutputStream(); 984 | bais = new java.io.ByteArrayInputStream(bytes); 985 | gzis = new java.util.zip.GZIPInputStream(bais); 986 | 987 | while ((length = gzis.read(buffer)) >= 0) { 988 | baos.write(buffer, 0, length); 989 | } // end while: reading input 990 | 991 | // No error? Get new bytes. 992 | bytes = baos.toByteArray(); 993 | 994 | } // end try 995 | catch (java.io.IOException e) { 996 | // Just return originally-decoded bytes 997 | } // end catch 998 | finally { 999 | try { 1000 | baos.close(); 1001 | } catch (Exception e) { 1002 | } 1003 | try { 1004 | gzis.close(); 1005 | } catch (Exception e) { 1006 | } 1007 | try { 1008 | bais.close(); 1009 | } catch (Exception e) { 1010 | } 1011 | } // end finally 1012 | 1013 | } // end if: gzipped 1014 | } // end if: bytes.length >= 2 1015 | 1016 | return bytes; 1017 | } // end decode 1018 | 1019 | /** 1020 | * calculate local file based class path for class loader if possible (servlet classes must be located there) 1021 | * 1022 | * @param cl 1023 | * class loader 1024 | * @return class path in string 1025 | */ 1026 | static public String calculateClassPath(ClassLoader cl) { 1027 | // scan cl chain to find 1028 | StringBuffer classPath = new StringBuffer(); 1029 | boolean jspFound = false, servletFound = false; 1030 | while (cl != null) { 1031 | if (cl instanceof URLClassLoader) { 1032 | boolean addClasses = false; 1033 | if (jspFound == false) { 1034 | jspFound = ((URLClassLoader) cl).findResource("javax/servlet/jsp/JspPage.class") != null; 1035 | addClasses |= jspFound; 1036 | } 1037 | if (servletFound == false) { 1038 | servletFound = ((URLClassLoader) cl).findResource("javax/servlet/http/HttpServlet.class") != null; 1039 | addClasses |= servletFound; 1040 | } 1041 | if (addClasses) { 1042 | URL[] urls = ((URLClassLoader) cl).getURLs(); 1043 | for (int i = 0; i < urls.length; i++) { 1044 | String classFile = toFile(urls[i]); 1045 | if (classFile == null) 1046 | continue; 1047 | if (classPath.length() > 0) 1048 | classPath.append(File.pathSeparatorChar).append(classFile); 1049 | else 1050 | classPath.append(classFile); 1051 | } 1052 | } 1053 | if (jspFound && servletFound) 1054 | return classPath.toString(); 1055 | } 1056 | cl = cl.getParent(); 1057 | } 1058 | return System.getProperty("java.class.path"); 1059 | } 1060 | 1061 | public static int parseInt(Object num, int def) { 1062 | if (num instanceof Number) 1063 | return ((Number)num).intValue(); 1064 | try { 1065 | return Integer.parseInt(num.toString()); 1066 | } catch (Exception e) { 1067 | return def; 1068 | } 1069 | } 1070 | 1071 | public static final String toFile(URL url) { 1072 | if (url.getProtocol().indexOf("file") < 0) 1073 | return null; 1074 | String result = url.getPath(); 1075 | if (result.charAt(0) == '/' && File.separatorChar == '\\') 1076 | result = result.substring(1); 1077 | try { 1078 | return decode(result, "UTF-8"); 1079 | } catch (UnsupportedEncodingException e) { 1080 | return result; 1081 | } 1082 | } 1083 | 1084 | // public static final int firstOccurrence(String s, String occur) { 1085 | // 1086 | // } 1087 | 1088 | public static interface ThreadFactory { 1089 | Thread create(Runnable runnable); 1090 | } 1091 | 1092 | public static final class ThreadPool { 1093 | static final int DEF_MAX_POOLED_THREAD = 20; 1094 | 1095 | static final String ID = "Acme.Utils.ThreadPool"; 1096 | 1097 | public static final String MAXNOTHREAD = ID + ".maxpooledthreads"; 1098 | 1099 | protected static int counter; 1100 | 1101 | protected ArrayList freeThreads; 1102 | 1103 | protected HashMap busyThreads; 1104 | 1105 | protected int maxThreads; 1106 | 1107 | protected ThreadFactory threadFactory; 1108 | 1109 | /** 1110 | * Creates a thread pool not queued with max number of threads defined in properties or DEF_MAX_POOLED_THREAD = 20 1111 | * 1112 | * @param Properties 1113 | * where property THREADSINPOOL gives max threads Note if THREADSINPOOL not integers, or negative then DEF_MAX_POOLED_THREAD used 1114 | */ 1115 | public ThreadPool(Properties properties, ThreadFactory threadfactory) { 1116 | maxThreads = parseInt(properties.getProperty(MAXNOTHREAD), DEF_MAX_POOLED_THREAD); 1117 | if (maxThreads < 0) 1118 | maxThreads = DEF_MAX_POOLED_THREAD; 1119 | freeThreads = new ArrayList(maxThreads); 1120 | busyThreads = new HashMap(maxThreads); 1121 | this.threadFactory = threadfactory; 1122 | } 1123 | 1124 | /** 1125 | * Assigns a new value for max threads 1126 | * 1127 | * @param int 1128 | * new value of max threads, can't be less than 2, but can be 0 If current number threads exceed the value, then extra thread will be 1129 | * discarded gracefully 1130 | */ 1131 | public void setMaxThreads(int newSize) { 1132 | if (newSize > 2 || newSize == 0) 1133 | maxThreads = newSize; 1134 | } 1135 | 1136 | /** 1137 | * Returns setting for max number of threads 1138 | * 1139 | * @return int setting for max number of threads, doesn't reflect actual number of threads though 1140 | */ 1141 | public int getMaxThreads() { 1142 | return maxThreads; 1143 | } 1144 | 1145 | /** 1146 | * Takes a new task for execution by a threads in pool will wait until free threads if number of threads reached max 1147 | * 1148 | * @param Runnable 1149 | * task for execution 1150 | */ 1151 | public void executeThread(Runnable runnable) { 1152 | PooledThread pt = null; 1153 | do { 1154 | synchronized (freeThreads) { 1155 | if (freeThreads.size() > 0) 1156 | pt = (PooledThread) freeThreads.remove(0); 1157 | } 1158 | if (pt != null && pt.isAlive() == false) 1159 | pt = null; 1160 | if (pt == null) 1161 | synchronized (busyThreads) { 1162 | if (busyThreads.size() < maxThreads || maxThreads == 0) 1163 | pt = new PooledThread(); 1164 | } 1165 | if (pt == null) 1166 | synchronized (freeThreads) { 1167 | try { 1168 | freeThreads.wait(); 1169 | } catch (InterruptedException ie) { 1170 | } 1171 | } 1172 | } while (pt == null); 1173 | pt.setName("-PooledThread: " + runnable); 1174 | pt.setRunner(runnable); 1175 | synchronized (busyThreads) { 1176 | busyThreads.put(pt, pt); 1177 | } 1178 | } 1179 | 1180 | protected void finalize() throws Throwable { 1181 | synchronized (freeThreads) { 1182 | Iterator i = freeThreads.iterator(); 1183 | while (i.hasNext()) 1184 | ((PooledThread) i.next()).interrupt(); 1185 | } 1186 | synchronized (busyThreads) { 1187 | Iterator i = freeThreads.iterator(); 1188 | while (i.hasNext()) 1189 | ((PooledThread) i.next()).interrupt(); 1190 | } 1191 | super.finalize(); 1192 | } 1193 | 1194 | public String toString() { 1195 | if (freeThreads != null && busyThreads != null) 1196 | return ID + ": free threads " + freeThreads.size() + " busy threads " + busyThreads.size(); 1197 | else 1198 | return ID + ": not initialized yet. " + super.toString(); 1199 | } 1200 | 1201 | class PooledThread implements Runnable { 1202 | 1203 | Runnable runner; 1204 | 1205 | boolean quit; 1206 | 1207 | Thread delegateThread; 1208 | 1209 | String id = ID + "(" + (counter++) + ")"; 1210 | 1211 | PooledThread() { 1212 | if (threadFactory != null) 1213 | delegateThread = threadFactory.create(this); 1214 | else 1215 | delegateThread = new Thread(this); 1216 | setName("-PooledThread: CREATED"); 1217 | delegateThread.start(); 1218 | } 1219 | 1220 | public void setName(String name) { 1221 | delegateThread.setName(id + name); 1222 | } 1223 | 1224 | public boolean isAlive() { 1225 | return delegateThread.isAlive(); 1226 | } 1227 | 1228 | synchronized public void run() { 1229 | do { 1230 | if (runner == null) 1231 | try { 1232 | this.wait(); 1233 | } catch (InterruptedException ie) { 1234 | 1235 | } 1236 | if (runner != null) { 1237 | try { 1238 | runner.run(); 1239 | } catch (Throwable t) { 1240 | if (t instanceof ThreadDeath) 1241 | throw (ThreadDeath) t; 1242 | t.printStackTrace(); 1243 | } finally { 1244 | runner = null; 1245 | } 1246 | 1247 | int activeThreads = 0; 1248 | synchronized (busyThreads) { 1249 | busyThreads.remove(this); 1250 | activeThreads = busyThreads.size(); 1251 | } 1252 | synchronized (freeThreads) { 1253 | if (freeThreads.size() + activeThreads > maxThreads) 1254 | break; // discard this thread 1255 | freeThreads.add(this); 1256 | delegateThread.setName(ID + "-PooledThread: FREE"); 1257 | freeThreads.notify(); 1258 | } 1259 | } 1260 | } while (!quit); 1261 | } 1262 | 1263 | synchronized public void interrupt() { 1264 | quit = true; 1265 | delegateThread.interrupt(); 1266 | } 1267 | 1268 | synchronized void setRunner(Runnable runnable) { 1269 | if (runner != null) 1270 | throw new RuntimeException("Invalid worker thread state, current runner not null."); 1271 | runner = runnable; 1272 | this.notifyAll(); 1273 | } 1274 | } 1275 | } 1276 | 1277 | public static class DummyPrintStream extends PrintStream { 1278 | public DummyPrintStream() { 1279 | super(new OutputStream() { 1280 | public void write(int i) { 1281 | } 1282 | }); 1283 | } 1284 | } 1285 | 1286 | public static class SimpleBuffer { 1287 | byte[] buffer; 1288 | 1289 | int fillPos; 1290 | 1291 | byte[] emptyBuffer; 1292 | 1293 | public SimpleBuffer() { 1294 | fillPos = 0; 1295 | setSize(COPY_BUF_SIZE); 1296 | } 1297 | 1298 | public synchronized void setSize(int size) { 1299 | if (size < 0) 1300 | throw new IllegalArgumentException("Size can't be negative"); 1301 | if (fillPos <= 0) 1302 | buffer = new byte[size]; 1303 | else 1304 | throw new IllegalStateException("Can't resize buffer containing data"); 1305 | } 1306 | 1307 | public synchronized int getSize() { 1308 | return buffer.length; 1309 | } 1310 | 1311 | public synchronized byte[] put(byte[] data, int off, int len) { 1312 | //System.err.println("put in buff:" + len+", fp:"+fillPos); 1313 | if (buffer.length > fillPos + len) { 1314 | System.arraycopy(data, off, buffer, fillPos, len); 1315 | fillPos += len; 1316 | return getEmptyBuffer(); 1317 | } 1318 | byte[] result = new byte[Math.max(fillPos + len - buffer.length, buffer.length)]; 1319 | //System.err.println("fp:" + fillPos + ",bl:" + buffer.length + ",rl:" + result.length + ",l:" + len); 1320 | // fill result 1321 | int rfilled = 0; 1322 | if (fillPos < result.length) { 1323 | System.arraycopy(buffer, 0, result, 0, fillPos); 1324 | rfilled = result.length - fillPos; 1325 | System.arraycopy(data, off, result, fillPos, rfilled); 1326 | fillPos = 0; 1327 | //System.err.println("1rf:"+rfilled); 1328 | } else { 1329 | System.arraycopy(buffer, 0, result, 0, result.length); 1330 | System.arraycopy(buffer, result.length, buffer, 0, fillPos - result.length); 1331 | fillPos -= result.length; 1332 | rfilled = 0; 1333 | //System.err.println("qrf: 0"); 1334 | } 1335 | if (rfilled < len) { 1336 | System.arraycopy(data, off + rfilled, buffer, fillPos, len - rfilled); 1337 | fillPos += len - rfilled; 1338 | //System.err.println("added to buf:"+(len - rfilled)); 1339 | } 1340 | return result; 1341 | } 1342 | 1343 | public synchronized byte[] get() { 1344 | //System.err.println("get fp: "+fillPos); 1345 | if (fillPos <= 0) { 1346 | return getEmptyBuffer(); 1347 | } 1348 | byte[] result = new byte[fillPos]; 1349 | System.arraycopy(buffer, 0, result, 0, fillPos); 1350 | fillPos = 0; 1351 | return result; 1352 | } 1353 | 1354 | public synchronized void reset() { 1355 | //System.err.println("reset buf"); 1356 | fillPos = 0; 1357 | } 1358 | 1359 | private synchronized byte[] getEmptyBuffer() { 1360 | if (emptyBuffer == null) 1361 | emptyBuffer = new byte[0]; 1362 | return emptyBuffer; 1363 | } 1364 | } 1365 | 1366 | public static void main(String[] args) { 1367 | try { 1368 | System.out.println(args[0]); 1369 | System.out.println(canonicalizePath(args[0])); 1370 | } catch (Exception e) { 1371 | e.printStackTrace(); 1372 | } 1373 | } 1374 | } 1375 | -------------------------------------------------------------------------------- /vendor/tjws/src/java/Acme/WildcardDictionary.java: -------------------------------------------------------------------------------- 1 | // WildcardDictionary - a dictionary with wildcard lookups 2 | // 3 | // Copyright (C) 1996 by Jef Poskanzer . All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | // SUCH DAMAGE. 25 | // 26 | // Visit the ACME Labs Java page for up-to-date versions of this and other 27 | // fine Java utilities: http://www.acme.com/java/ 28 | // 29 | // http://tjws.sourceforge.net 30 | // $Id: WildcardDictionary.java,v 1.3 2006/06/16 08:40:04 rogatkin Exp $ 31 | 32 | package Acme; 33 | 34 | import java.util.Dictionary; 35 | import java.util.Enumeration; 36 | import java.util.Vector; 37 | 38 | /// A dictionary with wildcard lookups. 39 | //

40 | // The keys in this dictionary are wildcard patterns. When you do a get(), 41 | // the string you pass in is matched against all the patterns, and the 42 | // first match is returned. 43 | //

44 | // The wildcard matcher is fairly simple, it implements * meaning any 45 | // string, ? meaning any single character, and | separating multiple 46 | // patterns. All other characters must match literally. 47 | //

48 | // Fetch the software.
49 | // Fetch the entire Acme package. 50 | //

51 | // @see Acme.Utils#match 52 | 53 | public class WildcardDictionary extends Dictionary { 54 | 55 | private Vector keys; 56 | 57 | private Vector elements; 58 | 59 | // / Constructor. 60 | public WildcardDictionary() { 61 | keys = new Vector(); 62 | elements = new Vector(); 63 | } 64 | 65 | // / Returns the number of elements contained within the dictionary. 66 | public int size() { 67 | return elements.size(); 68 | } 69 | 70 | // / Returns true if the dictionary contains no elements. 71 | public boolean isEmpty() { 72 | return size() == 0; 73 | } 74 | 75 | // / Returns an enumeration of the dictionary's keys. 76 | public Enumeration keys() { 77 | return keys.elements(); 78 | } 79 | 80 | // / Returns an enumeration of the elements. Use the Enumeration methods 81 | // on the returned object to fetch the elements sequentially. 82 | public Enumeration elements() { 83 | return elements.elements(); 84 | } 85 | 86 | // / Gets the object associated with the specified key in the dictionary. 87 | // The key is assumed to be a String, which is matched against 88 | // the wildcard-pattern keys in the dictionary. 89 | // @param key the string to match 90 | // @returns the element for the key, or null if there's no match 91 | // @see Acme.Utils#match 92 | public synchronized Object get(Object key) { 93 | String sKey = (String) key; 94 | int matching_len = 0, found = -1; 95 | // to optimize speed, keys should be sorted by length 96 | // TODO: above 97 | for (int i = keys.size() - 1; i > -1; i--) { 98 | String thisKey = (String) keys.elementAt(i); 99 | int current = Acme.Utils.matchSpan(thisKey, sKey); 100 | if (current > matching_len) { 101 | found = i; 102 | matching_len = current; 103 | } 104 | } 105 | if (found > -1) 106 | return elements.elementAt(found); 107 | return null; 108 | } 109 | 110 | public static String trimPathSeparators(String src) { 111 | StringBuffer result = new StringBuffer(src.length()); 112 | boolean ms = false; 113 | for (int i = 0; i < src.length(); i++) { 114 | char c = src.charAt(i); 115 | if (c == '/' || c == '\\') { 116 | if (!ms) { 117 | result.append(c); 118 | ms = true; 119 | } 120 | } else { 121 | result.append(c); 122 | ms = false; 123 | } 124 | } 125 | return result.toString(); 126 | } 127 | 128 | // / Puts the specified element into the Dictionary, using the specified 129 | // key. The element may be retrieved by doing a get() with the same 130 | // key. The key and the element cannot be null. 131 | // @param key the specified wildcard-pattern key 132 | // @param value the specified element 133 | // @return the old value of the key, or null if it did not have one. 134 | // @exception NullPointerException If the value of the specified 135 | // element is null. 136 | public synchronized Object put(Object key, Object element) { 137 | int i = keys.indexOf(key); 138 | if (i != -1) { 139 | Object oldElement = elements.elementAt(i); 140 | elements.setElementAt(element, i); 141 | return oldElement; 142 | } else { 143 | keys.addElement(key); 144 | elements.addElement(element); 145 | return null; 146 | } 147 | } 148 | 149 | // / Removes the element corresponding to the key. Does nothing if the 150 | // key is not present. 151 | // @param key the key that needs to be removed 152 | // @return the value of key, or null if the key was not found. 153 | public synchronized Object remove(Object key) { 154 | int i = keys.indexOf(key); 155 | if (i != -1) { 156 | Object oldElement = elements.elementAt(i); 157 | keys.removeElementAt(i); 158 | elements.removeElementAt(i); 159 | return oldElement; 160 | } else 161 | return null; 162 | } 163 | } 164 | --------------------------------------------------------------------------------