You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2020/07/05 11:41:42 UTC

svn commit: r1879521 [37/37] - in /river/jtsk/modules/modularize/apache-river: ./ browser/ browser/src/main/java/org/apache/river/example/browser/ extra/ groovy-config/ river-activation/ river-collections/ river-collections/src/main/java/org/apache/riv...

Modified: river/jtsk/modules/modularize/apache-river/tools/checkconfigurationfile/pom.xml
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/checkconfigurationfile/pom.xml?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/checkconfigurationfile/pom.xml (original)
+++ river/jtsk/modules/modularize/apache-river/tools/checkconfigurationfile/pom.xml Sun Jul  5 11:41:39 2020
@@ -1,75 +1,77 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-~ Copyright (C) 2014 the original author or authors.
-~
-~ Licensed under the Apache License, Version 2.0 (the "License");
-~ you may not use this file except in compliance with the License.
-~ You may obtain a copy of the License at
-~
-~ http://www.apache.org/licenses/LICENSE-2.0
-~
-~ Unless required by applicable law or agreed to in writing, software
-~ distributed under the License is distributed on an "AS IS" BASIS,
-~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-~ See the License for the specific language governing permissions and
-~ limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.apache.river</groupId>
-        <artifactId>tools</artifactId>
-        <version>3.0-SNAPSHOT</version>
-    </parent>
+<!-- ~ Copyright (C) 2014 the original author or authors. ~ ~ Licensed under 
+	the Apache License, Version 2.0 (the "License"); ~ you may not use this file 
+	except in compliance with the License. ~ You may obtain a copy of the License 
+	at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by 
+	applicable law or agreed to in writing, software ~ distributed under the 
+	License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. ~ See the License for the specific 
+	language governing permissions and ~ limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.river</groupId>
+		<artifactId>tools</artifactId>
+		<version>3.0-SNAPSHOT</version>
+	</parent>
 
-    <groupId>org.apache.river.tools</groupId>
-    <artifactId>checkconfigurationfile</artifactId>
+	<groupId>org.apache.river.tools</groupId>
+	<artifactId>checkconfigurationfile</artifactId>
 
-    <name>Tool :: Check ConfigurationFile</name>
-    <description>Checks the format of the source for a Jini ConfigurationFile. The source
+	<name>Tool :: Check ConfigurationFile</name>
+	<description>Checks the format of the source for a Jini ConfigurationFile. The source
          is specified with either a file, URL, or standard input, as well as with
          override options. The checks include syntax and static type checking, and
          require access to any application types mentioned in the source.
     </description>
 
-    <dependencies>       
-        <dependency>
-            <groupId>org.apache.river</groupId>
-            <artifactId>river-url-integrity</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <configuration>
-                    <archive>                
-                        <manifestEntries>
-                            <Main-Class>org.apache.river.tool.CheckConfigurationFile</Main-Class>
-                            <Implementation-Version>${project.version}</Implementation-Version>
-                        </manifestEntries>
-                    </archive>
-                </configuration>
-            </plugin>  
-
-             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.1.1</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>jar-no-fork</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-url-integrity</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-platform</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestEntries>
+							<Main-Class>org.apache.river.tool.CheckConfigurationFile</Main-Class>
+							<Implementation-Version>${project.version}</Implementation-Version>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>2.1.1</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>jar-no-fork</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
 </project>

Modified: river/jtsk/modules/modularize/apache-river/tools/classdep/pom.xml
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/classdep/pom.xml?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/classdep/pom.xml (original)
+++ river/jtsk/modules/modularize/apache-river/tools/classdep/pom.xml Sun Jul  5 11:41:39 2020
@@ -1,33 +1,28 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-~ Copyright (C) 2014 the original author or authors.
-~
-~ Licensed under the Apache License, Version 2.0 (the "License");
-~ you may not use this file except in compliance with the License.
-~ You may obtain a copy of the License at
-~
-~ http://www.apache.org/licenses/LICENSE-2.0
-~
-~ Unless required by applicable law or agreed to in writing, software
-~ distributed under the License is distributed on an "AS IS" BASIS,
-~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-~ See the License for the specific language governing permissions and
-~ limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.apache.river</groupId>
-        <artifactId>tools</artifactId>
-        <version>3.0-SNAPSHOT</version>
-    </parent>
+<!-- ~ Copyright (C) 2014 the original author or authors. ~ ~ Licensed under 
+	the Apache License, Version 2.0 (the "License"); ~ you may not use this file 
+	except in compliance with the License. ~ You may obtain a copy of the License 
+	at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by 
+	applicable law or agreed to in writing, software ~ distributed under the 
+	License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. ~ See the License for the specific 
+	language governing permissions and ~ limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.river</groupId>
+		<artifactId>tools</artifactId>
+		<version>3.0-SNAPSHOT</version>
+	</parent>
 
-    <groupId>org.apache.river.tools</groupId>
-    <artifactId>classdep</artifactId>
+	<groupId>org.apache.river.tools</groupId>
+	<artifactId>classdep</artifactId>
 
-    <name>Tool :: ClassDep</name>
-    <description>Tool used to analyze a set of classes and determine on what other classes
+	<name>Tool :: ClassDep</name>
+	<description>Tool used to analyze a set of classes and determine on what other classes
       they directly or indirectly depend. Typically this tool is used to
       compute the necessary and sufficient set of classes to include in a JAR
       file, for use in the class path of a client or service, or for use in the
@@ -42,49 +37,58 @@
       those classes.
     </description>
 
-    <dependencies>      
-        <dependency>
-            <groupId>org.ow2.asm</groupId>
-            <artifactId>asm</artifactId>
-            <version>5.0.1</version>
-        </dependency>
-        <dependency>
-            <groupId>org.ow2.asm</groupId>
-            <artifactId>asm-commons</artifactId>
-            <version>5.0.1</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <configuration>
-                    <archive>                
-                        <manifestEntries>
-                            <Main-Class>org.apache.river.tool.ClassDep</Main-Class>
-                            <Implementation-Version>${project.version}</Implementation-Version>
-                            <Class-Path>asm-5.0.1.jar asm-commons-5.0.1.jar</Class-Path>
-                        </manifestEntries>
-                    </archive>
-                </configuration>
-            </plugin>  
-
-             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.1.1</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>jar-no-fork</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
+	<dependencies>
+
+		<dependency>
+			<groupId>org.ow2.asm</groupId>
+			<artifactId>asm</artifactId>
+			<version>5.0.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.ow2.asm</groupId>
+			<artifactId>asm-commons</artifactId>
+			<version>5.0.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-platform</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestEntries>
+							<Main-Class>org.apache.river.tool.ClassDep</Main-Class>
+							<Implementation-Version>${project.version}</Implementation-Version>
+							<Class-Path>asm-5.0.1.jar asm-commons-5.0.1.jar</Class-Path>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>2.1.1</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>jar-no-fork</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
 </project>

Modified: river/jtsk/modules/modularize/apache-river/tools/classserver/pom.xml
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/classserver/pom.xml?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/classserver/pom.xml (original)
+++ river/jtsk/modules/modularize/apache-river/tools/classserver/pom.xml Sun Jul  5 11:41:39 2020
@@ -1,78 +1,80 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-~ Copyright (C) 2014 the original author or authors.
-~
-~ Licensed under the Apache License, Version 2.0 (the "License");
-~ you may not use this file except in compliance with the License.
-~ You may obtain a copy of the License at
-~
-~ http://www.apache.org/licenses/LICENSE-2.0
-~
-~ Unless required by applicable law or agreed to in writing, software
-~ distributed under the License is distributed on an "AS IS" BASIS,
-~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-~ See the License for the specific language governing permissions and
-~ limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.apache.river</groupId>
-        <artifactId>tools</artifactId>
-        <version>3.0-SNAPSHOT</version>
-    </parent>
-
-    <groupId>org.apache.river.tools</groupId>
-    <artifactId>classserver</artifactId>
-
-    <name>Tool :: Class Server</name>
-    <description>A simple HTTP server, for serving up JAR and class files. </description>
-
-    <dependencies>       
-
-        <dependency>
-            <groupId>org.apache.river</groupId>
-            <artifactId>river-url-integrity</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.river</groupId>
-            <artifactId>river-lib</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <configuration>
-                    <archive>                
-                        <manifestEntries>
-                            <Main-Class>org.apache.river.tool.ClassServer</Main-Class>
-                            <Implementation-Version>${project.version}</Implementation-Version>
-                        </manifestEntries>
-                    </archive>
-                </configuration>
-            </plugin>  
-
-             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.1.1</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>jar-no-fork</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
+<!-- ~ Copyright (C) 2014 the original author or authors. ~ ~ Licensed under 
+	the Apache License, Version 2.0 (the "License"); ~ you may not use this file 
+	except in compliance with the License. ~ You may obtain a copy of the License 
+	at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by 
+	applicable law or agreed to in writing, software ~ distributed under the 
+	License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. ~ See the License for the specific 
+	language governing permissions and ~ limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.river</groupId>
+		<artifactId>tools</artifactId>
+		<version>3.0-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.apache.river.tools</groupId>
+	<artifactId>classserver</artifactId>
+
+	<name>Tool :: Class Server</name>
+	<description>A simple HTTP server, for serving up JAR and class files. </description>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-url-integrity</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-lib</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.river</groupId>
+			<artifactId>river-platform</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestEntries>
+							<Main-Class>org.apache.river.tool.ClassServer</Main-Class>
+							<Implementation-Version>${project.version}</Implementation-Version>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>2.1.1</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>jar-no-fork</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
 </project>

Modified: river/jtsk/modules/modularize/apache-river/tools/classserver/src/main/java/org/apache/river/tool/ClassServer.java
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/classserver/src/main/java/org/apache/river/tool/ClassServer.java?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/classserver/src/main/java/org/apache/river/tool/ClassServer.java (original)
+++ river/jtsk/modules/modularize/apache-river/tools/classserver/src/main/java/org/apache/river/tool/ClassServer.java Sun Jul  5 11:41:39 2020
@@ -1,905 +1,905 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.river.tool;
-
-import org.apache.river.config.LocalHostLookup;
-import org.apache.river.logging.Levels;
-import org.apache.river.start.LifeCycle;
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.BindException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.StringTokenizer;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.apache.river.api.util.Startable;
-
-/**
- * A simple HTTP server, for serving up JAR and class files.
- * <p>
- * The following items are discussed below:
- * <ul>
- * <li>{@linkplain #main Command line options}
- * <li><a href="#logging">Logging</a>
- * <li><a href="#running">Examples for running ClassServer</a>
- * </ul>
- * <p>
- * <a name="logging"><h3>Logging</h3></a>
- * <p>
- *
- * This implementation uses the {@link Logger} named
- * <code>org.apache.river.tool.ClassServer</code> to log information at the
- * following logging levels:
- * <p>
- * <table border="1" cellpadding="5"
- *         summary="Describes logging performed by ClassServer at different
- *	   logging levels">
- * <caption halign="center" valign="top"><b><code>
- *    org.apache.river.tool.ClassServer</code></b></caption>
- *
- * <tr> <th scope="col">Level</th> <th scope="col">Description</th> </tr>
- * <tr>
- *   <td>{@link Level#SEVERE SEVERE}</td>
- *   <td>failure to accept an incoming connection</td>
- * </tr>
- * <tr>
- *   <td>{@link Level#WARNING WARNING}</td>
- *   <td>failure to read the contents of a requested file,
- *       failure to find the message resource bundle, failure while
- *       executing the <code>-stop</code> option
- *   </td>
- * </tr>
- * <tr>
- *   <td>{@link Level#INFO INFO}</td>
- *   <td>server startup and termination</td>
- * </tr>
- * <tr>
- *   <td>{@link Level#CONFIG CONFIG}</td>
- *   <td>the JAR files being used for <code>-trees</code></td>
- * </tr>
- * <tr>
- *   <td>{@link Levels#HANDLED HANDLED}</td>
- *   <td>failure reading an HTTP request or writing a response</td>
- * </tr>
- * <tr>
- *   <td>{@link Level#FINE FINE}</td>
- *   <td>bad HTTP requests, HTTP requests for nonexistent files</td>
- * </tr>
- * <tr>
- *   <td>{@link Level#FINER FINER}</td>
- *   <td>good HTTP requests</td>
- * </tr>
- * </table>
- *
- * <p>
- * <a name="running"><h3>Examples for running ClassServer</h3></a>
- * <p>
- *
- * This server can be run directly from the 
- * {@linkplain #main command line}
- * or as a nonactivatable service under the 
- * {@linkplain org.apache.river.start.ServiceStarter}.
- * <p>
- * An example of running directly from the command line is:
- * <blockquote><pre>
- * % java -jar <var><b>install_dir</b></var>/lib/classserver.jar \
- *        -port 8081 -dir <var><b>install_dir</b></var>/lib-dl -verbose
- * </pre></blockquote>
- * where <var><b>install_dir</b></var>
- * is the directory where the Apache River release is installed.
- * This command places the class server on the (non-default) port 
- * 8081, which  serves out the files under the (non-default) directory  
- * <var><b>install_dir</b></var>/lib-dl. The <code>-verbose</code> option
- * also causes download attempts to be logged.
- * <p>
- * An example of running under the Service Starter is:
- * <blockquote><pre>
- * % java -Djava.security.policy=<var><b>start_policy</b></var> \
- *        -jar <var><b>install_dir</b></var>/lib/start.jar \
- *        <a href="#config">httpd.config</a>
- * </pre></blockquote>
- * <p>
- * where <var><b>start_policy</b></var> is the name of a security
- * policy file (not provided), and <code>httpd.config</code> is the
- * following configuration file:
- * <a name="config"></a>
- * <blockquote><pre>
- * import org.apache.river.start.NonActivatableServiceDescriptor;
- * import org.apache.river.start.ServiceDescriptor;
- * 
- * org.apache.river.start {
- * 
- *   serviceDescriptors = new ServiceDescriptor[]{
- *     new NonActivatableServiceDescriptor(
- *       "",
- *       "<var><b>httpd_policy</b></var>",
- *       "<var><b>install_dir</b></var>/lib/classserver.jar",
- *       "org.apache.river.tool.ClassServer",
- *       new String[]{"-port", "8081", "-dir", "<var><b>install_dir</b></var>/lib-dl", "-verbose"})
- *     };
- * }
- * </pre></blockquote>
- * where <var><b>httpd_policy</b></var> is the name of a security
- * policy file (not provided).
- *
- * @author Sun Microsystems, Inc.
- *
- */
-public class ClassServer extends Thread implements Startable {
-    /** Default HTTP port */
-    private static int DEFAULT_PORT = 8080;
-    /** Default directory to serve files from on non-Windows OS */
-    private static String DEFAULT_DIR = "/vob/jive/lib-dl";
-    /** Default directory to serve files from on Windows */
-    private static String DEFAULT_WIN_DIR = "J:";
-    private static Logger logger =
-			    Logger.getLogger("org.apache.river.tool.ClassServer");
-
-    /** Server socket to accept connections on */
-    private final ServerSocket server;
-    /** Directories to serve from */
-    private final String[] dirs;
-    /** Map from String (JAR root) to JarFile[] (JAR class path) */
-    private final Map<String, JarFile[]> map;
-    /** Verbosity flag */
-    private volatile boolean verbose;
-    /** Stoppable flag */
-    private final boolean stoppable;
-    /** Read permission on dir and all subdirs, for each dir in dirs */
-    private final FilePermission[] perms;
-    /** Life cycle control */
-    private final LifeCycle lifeCycle;
-
-    /**
-     * Construct a server that does not support network shutdown.
-     * Use the {@link #start start} method to run it.
-     *
-     * @param port the port to use
-     * @param dirlist the list of directories to serve files from, with entries
-     * separated by the {@linkplain File#pathSeparatorChar path-separator
-     * character}
-     * @param trees <code>true</code> if files within JAR files should be
-     * served up
-     * @param verbose <code>true</code> if downloads should be logged
-     * @throws IOException if the server socket cannot be created
-     * @throws NullPointerException if <code>dir</code> is <code>null</code>
-     */
-    public ClassServer(int port,
-		       String dirlist,
-		       boolean trees,
-		       boolean verbose)
-	throws IOException
-    {
-	this(port, dirlist, trees, verbose, false, null);
-    }
-
-    /**
-     * Construct a server.  Use the {@link #start start} method to run it.
-     *
-     * @param port the port to use
-     * @param dirlist the list of directories to serve files from, with entries
-     * separated by the {@linkplain File#pathSeparatorChar path-separator
-     * character}
-     * @param trees <code>true</code> if files within JAR files should be
-     * served up
-     * @param verbose <code>true</code> if downloads should be logged
-     * @param stoppable <code>true</code> if network shutdown from the
-     * local host should be supported
-     * @throws IOException if the server socket cannot be created
-     * @throws NullPointerException if <code>dir</code> is <code>null</code>
-     */
-    public ClassServer(int port,
-		       String dirlist,
-		       boolean trees,
-		       boolean verbose,
-		       boolean stoppable)
-	throws IOException
-    {
-	this(port, dirlist, trees, verbose, stoppable, null);
-    }
-    
-    private static class Initializer {
-        int port;
-        String dirlist;
-        boolean trees;
-        boolean verbose;
-        boolean stoppable;
-        LifeCycle lifeCycle;
-        
-        Initializer(LifeCycle lifeCycle, String[] args){
-            port = DEFAULT_PORT;
-            dirlist = DEFAULT_DIR;
-            if (File.separatorChar == '\\') dirlist = DEFAULT_WIN_DIR;
-            trees = false;
-            verbose = false;
-            stoppable = false;
-            for (int i = 0; i < args.length ; i++ ) {
-                String arg = args[i];
-                if (arg.equals("-port")) {
-                    i++;
-                    port = Integer.parseInt(args[i]);
-                } else if (arg.equals("-dir") || arg.equals("-dirs")) {
-                    i++;
-                    dirlist = args[i];
-                } else if (arg.equals("-verbose")) {
-                    verbose = true;
-                } else if (arg.equals("-trees")) {
-                    trees = true;
-                } else if (arg.equals("-stoppable")) {
-                    stoppable = true;
-                } else {
-                    throw new IllegalArgumentException(arg);
-                }
-            }
-        }
-    }
-    
-    private ClassServer(Initializer init) throws IOException {
-        this(init.port, init.dirlist, init.trees, init.verbose, init.stoppable, init.lifeCycle);
-    }
-
-    /**
-     * Do the real work of the constructor.
-     */
-    private ClassServer(int port,
-		      String dirlist,
-		      boolean trees,
-		      boolean verbose,
-		      boolean stoppable,
-		      LifeCycle lifeCycle)
-	throws IOException
-    {
-	StringTokenizer st = new StringTokenizer(dirlist, File.pathSeparator);
-	dirs = new String[st.countTokens()];
-	perms = new FilePermission[dirs.length];
-	for (int i = 0; st.hasMoreTokens(); i++) {
-	    String dir = st.nextToken();
-	    if (!dir.endsWith(File.separator))
-		dir = dir + File.separatorChar;
-	    dirs[i] = dir;
-	    perms[i] = new FilePermission(dir + '-', "read");
-	}
-	this.verbose = verbose;
-	this.stoppable = stoppable;
-	this.lifeCycle = lifeCycle;
-        server = new ServerSocket();
-        server.setReuseAddress(true);
-        server.setSoTimeout(300000); // 5 minutes
-        try {
-            server.bind(new InetSocketAddress(port));
-        } catch( BindException be ) {
-            IOException ioe = new IOException( "failure to bind to port: "+port );
-            ioe.initCause(be);
-            throw ioe ;
-        }
-	if (!trees) {
-            map = null;
-	    return;
-        }
-	map = new HashMap<String, JarFile[]>();
-	Map<String,JarFile> jfmap = new HashMap<String,JarFile>();
-	for (int i = 0; i < dirs.length; i++) {
-	    String[] files = new File(dirs[i]).list();
-	    if (files == null)
-		continue;
-	    for (int j = 0; j < files.length; j++) {
-		String jar = files[j];
-		if (!jar.endsWith(".jar") && !jar.endsWith(".zip"))
-		    continue;
-		String name = jar.substring(0, jar.length() - 4);
-		if (map.containsKey(name))
-		    continue;
-		List<JarFile> jflist = new ArrayList<JarFile>(1);
-		addJar(jar, jflist, jfmap);
-		map.put(name, jflist.toArray(new JarFile[jflist.size()]));
-	    }
-	}
-    }
-    
-    /**
-     * Construct a server, accepting the same command line options
-     * supported by {@link #main main}, except for the <code>-stop</code>
-     * option.
-     * 
-     * If constructed by {@link org.apache.river.start.ServiceStarter},
-     * {@link Commission#start() }, is called automatically, otherwise {@link #start()}
-     * must be called manually after construction.
-     *
-     * @param args command line options
-     * @param lifeCycle life cycle control object, or <code>null</code>
-     * @throws IOException if the server socket cannot be created
-     * @throws IllegalArgumentException if a command line option is not
-     * understood
-     * @throws NullPointerException if <code>args</code> or any element
-     * of <code>args</code> is <code>null</code>
-     * @see Startable
-     */
-    public ClassServer(String[] args, LifeCycle lifeCycle) throws IOException {
-	this(new Initializer(lifeCycle, args));
-    }
-
-    /** Add transitive Class-Path JARs to jflist. */
-    private void addJar(String jar, List<JarFile> jflist, Map<String,JarFile> jfmap)
-	throws IOException
-    {
-	JarFile jf = jfmap.get(jar);
-	if (jf != null) {
-	    if (jflist.contains(jf)) {
-		return;
-	    }
-	} else {
-	    for (int i = 0; i < dirs.length; i++) {
-		File f = new File(dirs[i] + jar).getCanonicalFile();
-		if (f.exists()) {
-		    jf = new JarFile(f);
-		    jfmap.put(jar, jf);
-		    if (verbose)
-			print("classserver.jar", f.getPath());
-		    logger.config(f.getPath());
-		    break;
-		}
-	    }
-	    if (jf == null) {
-		if (verbose)
-		    print("classserver.notfound", jar);
-		logger.log(Level.CONFIG, "{0} not found", jar);
-		return;
-	    }
-	}
-	jflist.add(jf);
-	Manifest man = jf.getManifest();
-	if (man == null)
-	    return;
-	Attributes attrs = man.getMainAttributes();
-	if (attrs == null)
-	    return;
-	String val = attrs.getValue(Attributes.Name.CLASS_PATH);
-	if (val == null)
-	    return;
-	for (StringTokenizer st = new StringTokenizer(val);
-	     st.hasMoreTokens(); )
-	{
-	    String elt = st.nextToken();
-	    String path = decode(elt);
-	    if (path == null) {
-		if (verbose)
-		    print("classserver.notfound", elt);
-		logger.log(Level.CONFIG, "{0} not found", elt);
-	    }
-	    if ('/' != File.separatorChar) {
-		path = path.replace('/', File.separatorChar);
-	    }
-	    addJar(path, jflist, jfmap);
-	}
-    }
-
-    /** Just keep looping, spawning a new thread for each incoming request. */
-    public void run() {
-	logger.log(Level.INFO, "ClassServer started [{0}, port {1}]",
-		   new Object[]{Arrays.asList(dirs),
-				Integer.toString(getPort())});
-	try {
-	    while (!isInterrupted()) {
-                try {
-                    new Task(server.accept()).start();
-                } catch (SocketTimeoutException e){
-                    // This happens every 5 minutes, it allows ClassServer to
-                    // be interrupted if necessary.
-                } catch ( SecurityException e){
-                    logger.log(Level.SEVERE, "Permission denied: ", e);
-                    interrupt();
-                }
-	    }
-	} catch (IOException e) {
-            if (verbose) {
-                e.printStackTrace();
-            }
-            if (!server.isClosed())
-                logger.log(Level.SEVERE, "accepting connection", e);
-        } finally {
-            terminate();
-        }
-    }
-
-    /** Close the server socket, causing the thread to terminate. */
-    public synchronized void terminate() {
-	verbose = false;
-	try {
-	    server.close();
-	} catch (IOException e) {
-	}
-	if (lifeCycle != null)
-	    lifeCycle.unregister(this);
-	logger.log(Level.INFO, "ClassServer terminated [port {0}]",
-		   Integer.toString(getPort()));
-    }
-
-    /** Returns the port on which this server is listening. */
-    public int getPort() {
-	return server.getLocalPort();
-    }
-
-    /** Read up to CRLF, return false if EOF */
-    private static boolean readLine(InputStream in, StringBuffer buf)
-	throws IOException
-    {
-	while (true) {
-	    int c = in.read();
-	    if (c < 0)
-		return buf.length() > 0;
-	    if (c == '\r') {
-		in.mark(1);
-		c = in.read();
-		if (c != '\n')
-		    in.reset();
-		return true;
-	    }
-	    if (c == '\n')
-		return true;
-	    buf.append((char) c);
-	}
-    }
-
-    /** Parse % HEX HEX from s starting at i */
-    private static char decode(String s, int i) {
-	return (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);
-    }
-
-    /** Decode escape sequences */
-    private static String decode(String path) {
-	try {
-	    for (int i = path.indexOf('%');
-		 i >= 0;
-		 i = path.indexOf('%', i + 1))
-	    {
-		char c = decode(path, i);
-		int n = 3;
-		if ((c & 0x80) != 0) {
-		    switch (c >> 4) {
-		    case 0xC:
-		    case 0xD:
-			n = 6;
-			c = (char)(((c & 0x1F) << 6) |
-				   (decode(path, i + 3) & 0x3F));
-			break;
-		    case 0xE:
-			n = 9;
-			c = (char)(((c & 0x0f) << 12) |
-				   ((decode(path, i + 3) & 0x3F) << 6) |
-				   (decode(path, i + 6) & 0x3F));
-			break;
-		    default:
-			return null;
-		    }
-		}
-		path = path.substring(0, i) + c + path.substring(i + n);
-	    }
-	} catch (Exception e) {
-	    return null;
-	}
-	return path;
-    }
-
-    /** Read the request/response and return the initial line. */
-    private static String getInput(Socket sock, boolean isRequest)
-	throws IOException
-    {
-	BufferedInputStream in =
-	    new BufferedInputStream(sock.getInputStream(), 256);
-	StringBuffer buf = new StringBuffer(80);
-	do {
-	    if (!readLine(in, buf))
-		return null;
-	} while (isRequest && buf.length() == 0);
-	String initial = buf.toString();
-	do {
-	    buf.setLength(0);
-	} while (readLine(in, buf) && buf.length() > 0);
-	return initial;
-    }
-
-    /** Simple daemon task thread */
-    private class Task extends Thread {
-	/** Socket for the incoming request */
-	private Socket sock;
-
-	/** Simple constructor */
-	public Task(Socket sock) {
-	    this.sock = sock;
-	    setDaemon(true);
-	}
-
-	/** Read specified number of bytes and always close the stream. */
-	private byte[] getBytes(InputStream in, long length)
-	    throws IOException
-	{
-	    DataInputStream din = new DataInputStream(in);
-	    byte[] bytes = new byte[(int)length];
-	    try {
-		din.readFully(bytes);
-	    } finally {
-		din.close();
-	    }
-	    return bytes;
-	}
-
-	/** Canonicalize the path */
-	private String canon(String path) {
-	    if (path.regionMatches(true, 0, "http://", 0, 7)) {
-		int i = path.indexOf('/', 7);
-		if (i < 0)
-		    path = "/";
-		else
-		    path = path.substring(i);
-	    }
-	    path = decode(path);
-	    if (path == null || path.length() == 0 || path.charAt(0) != '/')
-		return null;
-	    return path.substring(1);
-	}
-
-	/** Return the bytes of the requested file, or null if not found. */
-	private byte[] getBytes(String path) throws IOException {
-	    if (map != null) {
-		int i = path.indexOf('/');
-		if (i > 0) {
-		    JarFile[] jfs = (JarFile[])map.get(path.substring(0, i));
-		    if (jfs != null) {
-			String jpath = path.substring(i + 1);
-			for (i = 0; i < jfs.length; i++) {
-			    JarEntry je = jfs[i].getJarEntry(jpath);
-			    if (je != null)
-				return getBytes(jfs[i].getInputStream(je),
-						je.getSize());
-			}
-		    }
-		}
-	    }
-	    if ('/' != File.separatorChar) {
-		path = path.replace('/', File.separatorChar);
-	    }
-	    for (int i = 0; i < dirs.length; i++) {
-		File f = new File(dirs[i] + path);
-		if (perms[i].implies(new FilePermission(f.getPath(), "read")))
-		{
-		    try {
-			return getBytes(new FileInputStream(f), f.length());
-		    } catch (FileNotFoundException e) {
-		    }
-		}
-	    }
-	    return null;
-	}
-
-	/** Process the request */
-	public void run() {
-	    try {
-		DataOutputStream out =
-		    new DataOutputStream(sock.getOutputStream());
-		String req;
-		try {
-		    req = getInput(sock, true);
-		} catch (Exception e) {
-		    if (verbose) {
-			print("classserver.inputerror",
-			      new String[]{sock.getInetAddress().getHostName(),
-					   Integer.toString(sock.getPort())});
-			e.printStackTrace();
-		    }
-		    logger.log(Levels.HANDLED, "reading request", e);
-		    return;
-		}
-		if (req == null)
-		    return;
-		if (req.startsWith("SHUTDOWN *")) {
-		    if (verbose)
-			print("classserver.shutdown",
-			      new String[]{sock.getInetAddress().getHostName(),
-					   Integer.toString(sock.getPort())});
-		    boolean ok = stoppable;
-		    try {
-			new ServerSocket(0, 1, sock.getInetAddress());
-		    } catch (IOException e) {
-			ok = false;
-		    }
-		    if (!ok) {
-			out.writeBytes("HTTP/1.0 403 Forbidden\r\n\r\n");
-			out.flush();
-			return;
-		    }
-		    try {
-			out.writeBytes("HTTP/1.0 200 OK\r\n\r\n");
-			out.flush();
-		    } catch (Exception e) {
-			if (verbose)
-			    e.printStackTrace();
-			logger.log(Levels.HANDLED, "writing response", e);
-		    }
-		    terminate();
-		    return;
-		}
-		String[] args = null;
-		if (verbose || logger.isLoggable(Level.FINE))
-		    args = new String[]{req,
-					sock.getInetAddress().getHostName(),
-					Integer.toString(sock.getPort())};
-		boolean get = req.startsWith("GET ");
-		if (!get && !req.startsWith("HEAD ")) {
-		    if (verbose)
-			print("classserver.badrequest", args);
-		    logger.log(Level.FINE,
-			       "bad request \"{0}\" from {1}:{2}", args);
-		    out.writeBytes("HTTP/1.0 400 Bad Request\r\n\r\n");
-		    out.flush();
-		    return;
-		}
-		String path = req.substring(get ? 4 : 5);
-		int i = path.indexOf(' ');
-		if (i > 0)
-		    path = path.substring(0, i);
-		path = canon(path);
-		if (path == null) {
-		    if (verbose)
-			print("classserver.badrequest", args);
-		    logger.log(Level.FINE,
-			       "bad request \"{0}\" from {1}:{2}", args);
-		    out.writeBytes("HTTP/1.0 400 Bad Request\r\n\r\n");
-		    out.flush();
-		    return;
-		}
-		if (args != null)
-		    args[0] = path;
-		if (verbose) {
-		    print(get ? "classserver.request" : "classserver.probe",
-			  args);
-		}
-		logger.log(Level.FINER,
-			   get ?
-			   "{0} requested from {1}:{2}" :
-			   "{0} probed from {1}:{2}",
-			   args);
-		byte[] bytes;
-		try {
-		    bytes = getBytes(path);
-		} catch (Exception e) {
-		    if (verbose)
-			e.printStackTrace();
-		    logger.log(Level.WARNING, "getting bytes", e);
-		    out.writeBytes("HTTP/1.0 500 Internal Error\r\n\r\n");
-		    out.flush();
-		    return;
-		}
-		if (bytes == null) {
-		    if (verbose)
-			print("classserver.notfound", path);
-		    logger.log(Level.FINE, "{0} not found", path);
-		    out.writeBytes("HTTP/1.0 404 Not Found\r\n\r\n");
-		    out.flush();
-		    return;
-		}
-		out.writeBytes("HTTP/1.0 200 OK\r\n");
-		out.writeBytes("Content-Length: " + bytes.length + "\r\n");
-		out.writeBytes("Content-Type: application/java\r\n\r\n");
-		if (get)
-		    out.write(bytes);
-		out.flush();
-		if (get)
-		    fileDownloaded(path, sock.getInetAddress());
-	    } catch (Exception e) {
-		if (verbose)
-		    e.printStackTrace();
-		logger.log(Levels.HANDLED, "writing response", e);
-	    } finally {
-		try {
-		    sock.close();
-		} catch (IOException e) {
-		}
-	    }
-	}
-    }
-
-    private static ResourceBundle resources;
-    private static boolean resinit = false;
-
-    private static synchronized String getString(String key) {
-	if (!resinit) {
-	    resinit = true;
-	    try {
-		resources = ResourceBundle.getBundle("org.apache.river.tool.resources.classserver");
-	    } catch (MissingResourceException e) {
-		logger.log(Level.WARNING, "missing resource bundle {0}",
-			   "org.apache.river.tool.resources.classserver");
-	    }
-	}
-	if (resources != null) {
-	    try {
-		return resources.getString(key);
-	    } catch (MissingResourceException e) {
-	    }
-	}
-	return null;
-    }
-
-    private static void print(String key, String val) {
-	String fmt = getString(key);
-	if (fmt == null)
-	    fmt = "no text found: \"" + key + "\" {0}";
-	System.out.println(MessageFormat.format(fmt, new String[]{val}));
-    }
-
-    private static void print(String key, String[] vals) {
-	String fmt = getString(key);
-	if (fmt == null)
-	    fmt = "no text found: \"" + key + "\" {0} {1} {2}";
-	System.out.println(MessageFormat.format(fmt, vals));
-    }
-
-    /**
-     * This method provides a way for subclasses to be notified when a
-     * file has been completely downloaded.
-     * 
-     * @param fp The path to the file that was downloaded.
-     */
-    protected void fileDownloaded(String fp, InetAddress addr) {
-    }
-
-    /**
-     * Command line interface for creating an HTTP server.
-     * The command line options are:
-     * <pre>
-     * [-port <var>port</var>] [-dir <var>dirlist</var>] [-dirs <var>dirlist</var>] [-stoppable] [-verbose] [-trees]
-     * </pre>
-     * The default port is 8080; the default can be overridden with
-     * the <code>-port</code> option.  The default directory on Windows is
-     * <code>J:</code> and the default on other systems is
-     * <code>/vob/jive/lib-dl</code>; the default can be overridden with the
-     * <code>-dir</code> or <code>-dirs</code> option, providing one or more
-     * directories separated by the {@linkplain File#pathSeparatorChar
-     * path-separator character}. All files under these directories (including
-     * all subdirectories) are served up via HTTP.  If the pathname of a file
-     * is <var>path</var> relative to one of the top-level directories, then
-     * the file can be downloaded using the URL
-     * <pre>
-     * http://<var>host</var>:<var>port</var>/<var>path</var>
-     * </pre>
-     * If a relative <var>path</var> matches a file under more than one
-     * top-level directory, the file under the first top-level directory
-     * with a match is used. No caching of directory contents or file contents
-     * is performed. <p>
-     *
-     * If the <code>-stoppable</code> option is given, the HTTP server can be
-     * shut down with a custom HTTP <code>SHUTDOWN</code> request originating
-     * from the local host. The command line options for stopping an existing
-     * HTTP server are:
-     * <pre>
-     * [-port <var>port</var>] -stop
-     * </pre>
-     * <p>
-     *
-     * If the <code>-verbose</code> option is given, then all attempts to
-     * download files are output. <p>
-     * 
-     * The <code>-trees</code> option can be used to serve up individual files
-     * stored within JAR and zip files in addition to the files that are
-     * served up as described above. If the option is used, the server finds
-     * all JAR and zip files in the top-level directories (not in
-     * subdirectories).  If the name of the JAR or zip file is
-     * <var>name</var><code>.jar</code> or <var>name</var><code>.zip</code>,
-     * then any individual file named <var>file</var> within it (or within the
-     * JAR or zip files referenced transitively in <code>Class-Path</code>
-     * manifest attributes), can be downloaded using a URL of the form:
-     * <pre>
-     * http://<var>host</var>:<var>port</var>/<var>name</var>/<var>file</var>
-     * </pre>
-     * If multiple top-level directories have JAR or zip files with the same
-     * <var>name</var>, the file under the first top-level directory with a
-     * match is used. If a <code>Class-Path</code> element matches a file under
-     * more than one top-level directory, the file under the first top-level
-     * directory with a match is used. When this option is used, an open file
-     * descriptor and cached information is held for each JAR or zip file, for
-     * the life of the process.
-     */
-    public static void main(String[] args) {
-	int port = DEFAULT_PORT;
-	String dirlist = DEFAULT_DIR;
-	if (File.separatorChar == '\\')
-	    dirlist = DEFAULT_WIN_DIR;
-	boolean trees = false;
-	boolean verbose = false;
-	boolean stoppable = false;
-	boolean stop = false;
-	for (int i = 0; i < args.length ; i++ ) {
-	    String arg = args[i];
-	    if (arg.equals("-port")) {
-		i++;
-		port = Integer.parseInt(args[i]);
-	    } else if (arg.equals("-dir") || arg.equals("-dirs")) {
-		i++;
-		dirlist = args[i];
-	    } else if (arg.equals("-verbose")) {
-		verbose = true;
-	    } else if (arg.equals("-trees")) {
-		trees = true;
-	    } else if (arg.equals("-stoppable")) {
-		stoppable = true;
-	    } else if (arg.equals("-stop")) {
-		stop = true;
-	    } else {
-		print("classserver.usage", (String)null);
-		return;
-	    }
-	}
-	try {
-	    if (stop) {
-		Socket sock = new Socket(LocalHostLookup.getLocalHost(), port);
-		try {
-		    DataOutputStream out =
-			new DataOutputStream(sock.getOutputStream());
-		    out.writeBytes("SHUTDOWN *\r\n\r\n");
-		    out.flush();
-		    String status = getInput(sock, false);
-		    if (status != null && status.startsWith("HTTP/")) {
-			status = status.substring(status.indexOf(' ') + 1);
-			if (status.startsWith("403 ")) {
-			    print("classserver.forbidden", status);
-			} else if (!status.startsWith("200 ") &&
-				   status.indexOf(' ') == 3)
-			{
-			    print("classserver.status",
-				  new String[]{status.substring(0, 3),
-					       status.substring(4)});
-			}
-		    }
-		} finally {
-		    try {
-			sock.close();
-		    } catch (IOException e) {
-		    }
-		}
-	    } else {
-		new ClassServer(port, dirlist, trees, verbose,
-				stoppable).start();
-	    }
-	} catch (IOException e) {
-	    logger.log(Level.WARNING, "requesting shutdown", e);
-	}
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.tool;
+
+import org.apache.river.config.LocalHostLookup;
+import org.apache.river.logging.Levels;
+import org.apache.river.start.lifecycle.LifeCycle;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.BindException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.river.api.util.Startable;
+
+/**
+ * A simple HTTP server, for serving up JAR and class files.
+ * <p>
+ * The following items are discussed below:
+ * <ul>
+ * <li>{@linkplain #main Command line options}
+ * <li><a href="#logging">Logging</a>
+ * <li><a href="#running">Examples for running ClassServer</a>
+ * </ul>
+ * <p>
+ * <a name="logging"><h3>Logging</h3></a>
+ * <p>
+ *
+ * This implementation uses the {@link Logger} named
+ * <code>org.apache.river.tool.ClassServer</code> to log information at the
+ * following logging levels:
+ * <p>
+ * <table border="1" cellpadding="5"
+ *         summary="Describes logging performed by ClassServer at different
+ *	   logging levels">
+ * <caption halign="center" valign="top"><b><code>
+ *    org.apache.river.tool.ClassServer</code></b></caption>
+ *
+ * <tr> <th scope="col">Level</th> <th scope="col">Description</th> </tr>
+ * <tr>
+ *   <td>{@link Level#SEVERE SEVERE}</td>
+ *   <td>failure to accept an incoming connection</td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Level#WARNING WARNING}</td>
+ *   <td>failure to read the contents of a requested file,
+ *       failure to find the message resource bundle, failure while
+ *       executing the <code>-stop</code> option
+ *   </td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Level#INFO INFO}</td>
+ *   <td>server startup and termination</td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Level#CONFIG CONFIG}</td>
+ *   <td>the JAR files being used for <code>-trees</code></td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Levels#HANDLED HANDLED}</td>
+ *   <td>failure reading an HTTP request or writing a response</td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Level#FINE FINE}</td>
+ *   <td>bad HTTP requests, HTTP requests for nonexistent files</td>
+ * </tr>
+ * <tr>
+ *   <td>{@link Level#FINER FINER}</td>
+ *   <td>good HTTP requests</td>
+ * </tr>
+ * </table>
+ *
+ * <p>
+ * <a name="running"><h3>Examples for running ClassServer</h3></a>
+ * <p>
+ *
+ * This server can be run directly from the 
+ * {@linkplain #main command line}
+ * or as a nonactivatable service under the 
+ * {@linkplain org.apache.river.start.ServiceStarter}.
+ * <p>
+ * An example of running directly from the command line is:
+ * <blockquote><pre>
+ * % java -jar <var><b>install_dir</b></var>/lib/classserver.jar \
+ *        -port 8081 -dir <var><b>install_dir</b></var>/lib-dl -verbose
+ * </pre></blockquote>
+ * where <var><b>install_dir</b></var>
+ * is the directory where the Apache River release is installed.
+ * This command places the class server on the (non-default) port 
+ * 8081, which  serves out the files under the (non-default) directory  
+ * <var><b>install_dir</b></var>/lib-dl. The <code>-verbose</code> option
+ * also causes download attempts to be logged.
+ * <p>
+ * An example of running under the Service Starter is:
+ * <blockquote><pre>
+ * % java -Djava.security.policy=<var><b>start_policy</b></var> \
+ *        -jar <var><b>install_dir</b></var>/lib/start.jar \
+ *        <a href="#config">httpd.config</a>
+ * </pre></blockquote>
+ * <p>
+ * where <var><b>start_policy</b></var> is the name of a security
+ * policy file (not provided), and <code>httpd.config</code> is the
+ * following configuration file:
+ * <a name="config"></a>
+ * <blockquote><pre>
+ * import org.apache.river.start.NonActivatableServiceDescriptor;
+ * import org.apache.river.start.ServiceDescriptor;
+ * 
+ * org.apache.river.start {
+ * 
+ *   serviceDescriptors = new ServiceDescriptor[]{
+ *     new NonActivatableServiceDescriptor(
+ *       "",
+ *       "<var><b>httpd_policy</b></var>",
+ *       "<var><b>install_dir</b></var>/lib/classserver.jar",
+ *       "org.apache.river.tool.ClassServer",
+ *       new String[]{"-port", "8081", "-dir", "<var><b>install_dir</b></var>/lib-dl", "-verbose"})
+ *     };
+ * }
+ * </pre></blockquote>
+ * where <var><b>httpd_policy</b></var> is the name of a security
+ * policy file (not provided).
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+public class ClassServer extends Thread implements Startable {
+    /** Default HTTP port */
+    private static int DEFAULT_PORT = 8080;
+    /** Default directory to serve files from on non-Windows OS */
+    private static String DEFAULT_DIR = "/vob/jive/lib-dl";
+    /** Default directory to serve files from on Windows */
+    private static String DEFAULT_WIN_DIR = "J:";
+    private static Logger logger =
+			    Logger.getLogger("org.apache.river.tool.ClassServer");
+
+    /** Server socket to accept connections on */
+    private final ServerSocket server;
+    /** Directories to serve from */
+    private final String[] dirs;
+    /** Map from String (JAR root) to JarFile[] (JAR class path) */
+    private final Map<String, JarFile[]> map;
+    /** Verbosity flag */
+    private volatile boolean verbose;
+    /** Stoppable flag */
+    private final boolean stoppable;
+    /** Read permission on dir and all subdirs, for each dir in dirs */
+    private final FilePermission[] perms;
+    /** Life cycle control */
+    private final LifeCycle lifeCycle;
+
+    /**
+     * Construct a server that does not support network shutdown.
+     * Use the {@link #start start} method to run it.
+     *
+     * @param port the port to use
+     * @param dirlist the list of directories to serve files from, with entries
+     * separated by the {@linkplain File#pathSeparatorChar path-separator
+     * character}
+     * @param trees <code>true</code> if files within JAR files should be
+     * served up
+     * @param verbose <code>true</code> if downloads should be logged
+     * @throws IOException if the server socket cannot be created
+     * @throws NullPointerException if <code>dir</code> is <code>null</code>
+     */
+    public ClassServer(int port,
+		       String dirlist,
+		       boolean trees,
+		       boolean verbose)
+	throws IOException
+    {
+	this(port, dirlist, trees, verbose, false, null);
+    }
+
+    /**
+     * Construct a server.  Use the {@link #start start} method to run it.
+     *
+     * @param port the port to use
+     * @param dirlist the list of directories to serve files from, with entries
+     * separated by the {@linkplain File#pathSeparatorChar path-separator
+     * character}
+     * @param trees <code>true</code> if files within JAR files should be
+     * served up
+     * @param verbose <code>true</code> if downloads should be logged
+     * @param stoppable <code>true</code> if network shutdown from the
+     * local host should be supported
+     * @throws IOException if the server socket cannot be created
+     * @throws NullPointerException if <code>dir</code> is <code>null</code>
+     */
+    public ClassServer(int port,
+		       String dirlist,
+		       boolean trees,
+		       boolean verbose,
+		       boolean stoppable)
+	throws IOException
+    {
+	this(port, dirlist, trees, verbose, stoppable, null);
+    }
+    
+    private static class Initializer {
+        int port;
+        String dirlist;
+        boolean trees;
+        boolean verbose;
+        boolean stoppable;
+        LifeCycle lifeCycle;
+        
+        Initializer(LifeCycle lifeCycle, String[] args){
+            port = DEFAULT_PORT;
+            dirlist = DEFAULT_DIR;
+            if (File.separatorChar == '\\') dirlist = DEFAULT_WIN_DIR;
+            trees = false;
+            verbose = false;
+            stoppable = false;
+            for (int i = 0; i < args.length ; i++ ) {
+                String arg = args[i];
+                if (arg.equals("-port")) {
+                    i++;
+                    port = Integer.parseInt(args[i]);
+                } else if (arg.equals("-dir") || arg.equals("-dirs")) {
+                    i++;
+                    dirlist = args[i];
+                } else if (arg.equals("-verbose")) {
+                    verbose = true;
+                } else if (arg.equals("-trees")) {
+                    trees = true;
+                } else if (arg.equals("-stoppable")) {
+                    stoppable = true;
+                } else {
+                    throw new IllegalArgumentException(arg);
+                }
+            }
+        }
+    }
+    
+    private ClassServer(Initializer init) throws IOException {
+        this(init.port, init.dirlist, init.trees, init.verbose, init.stoppable, init.lifeCycle);
+    }
+
+    /**
+     * Do the real work of the constructor.
+     */
+    private ClassServer(int port,
+		      String dirlist,
+		      boolean trees,
+		      boolean verbose,
+		      boolean stoppable,
+		      LifeCycle lifeCycle)
+	throws IOException
+    {
+	StringTokenizer st = new StringTokenizer(dirlist, File.pathSeparator);
+	dirs = new String[st.countTokens()];
+	perms = new FilePermission[dirs.length];
+	for (int i = 0; st.hasMoreTokens(); i++) {
+	    String dir = st.nextToken();
+	    if (!dir.endsWith(File.separator))
+		dir = dir + File.separatorChar;
+	    dirs[i] = dir;
+	    perms[i] = new FilePermission(dir + '-', "read");
+	}
+	this.verbose = verbose;
+	this.stoppable = stoppable;
+	this.lifeCycle = lifeCycle;
+        server = new ServerSocket();
+        server.setReuseAddress(true);
+        server.setSoTimeout(300000); // 5 minutes
+        try {
+            server.bind(new InetSocketAddress(port));
+        } catch( BindException be ) {
+            IOException ioe = new IOException( "failure to bind to port: "+port );
+            ioe.initCause(be);
+            throw ioe ;
+        }
+	if (!trees) {
+            map = null;
+	    return;
+        }
+	map = new HashMap<String, JarFile[]>();
+	Map<String,JarFile> jfmap = new HashMap<String,JarFile>();
+	for (int i = 0; i < dirs.length; i++) {
+	    String[] files = new File(dirs[i]).list();
+	    if (files == null)
+		continue;
+	    for (int j = 0; j < files.length; j++) {
+		String jar = files[j];
+		if (!jar.endsWith(".jar") && !jar.endsWith(".zip"))
+		    continue;
+		String name = jar.substring(0, jar.length() - 4);
+		if (map.containsKey(name))
+		    continue;
+		List<JarFile> jflist = new ArrayList<JarFile>(1);
+		addJar(jar, jflist, jfmap);
+		map.put(name, jflist.toArray(new JarFile[jflist.size()]));
+	    }
+	}
+    }
+    
+    /**
+     * Construct a server, accepting the same command line options
+     * supported by {@link #main main}, except for the <code>-stop</code>
+     * option.
+     * 
+     * If constructed by {@link org.apache.river.start.ServiceStarter},
+     * {@link Commission#start() }, is called automatically, otherwise {@link #start()}
+     * must be called manually after construction.
+     *
+     * @param args command line options
+     * @param lifeCycle life cycle control object, or <code>null</code>
+     * @throws IOException if the server socket cannot be created
+     * @throws IllegalArgumentException if a command line option is not
+     * understood
+     * @throws NullPointerException if <code>args</code> or any element
+     * of <code>args</code> is <code>null</code>
+     * @see Startable
+     */
+    public ClassServer(String[] args, LifeCycle lifeCycle) throws IOException {
+	this(new Initializer(lifeCycle, args));
+    }
+
+    /** Add transitive Class-Path JARs to jflist. */
+    private void addJar(String jar, List<JarFile> jflist, Map<String,JarFile> jfmap)
+	throws IOException
+    {
+	JarFile jf = jfmap.get(jar);
+	if (jf != null) {
+	    if (jflist.contains(jf)) {
+		return;
+	    }
+	} else {
+	    for (int i = 0; i < dirs.length; i++) {
+		File f = new File(dirs[i] + jar).getCanonicalFile();
+		if (f.exists()) {
+		    jf = new JarFile(f);
+		    jfmap.put(jar, jf);
+		    if (verbose)
+			print("classserver.jar", f.getPath());
+		    logger.config(f.getPath());
+		    break;
+		}
+	    }
+	    if (jf == null) {
+		if (verbose)
+		    print("classserver.notfound", jar);
+		logger.log(Level.CONFIG, "{0} not found", jar);
+		return;
+	    }
+	}
+	jflist.add(jf);
+	Manifest man = jf.getManifest();
+	if (man == null)
+	    return;
+	Attributes attrs = man.getMainAttributes();
+	if (attrs == null)
+	    return;
+	String val = attrs.getValue(Attributes.Name.CLASS_PATH);
+	if (val == null)
+	    return;
+	for (StringTokenizer st = new StringTokenizer(val);
+	     st.hasMoreTokens(); )
+	{
+	    String elt = st.nextToken();
+	    String path = decode(elt);
+	    if (path == null) {
+		if (verbose)
+		    print("classserver.notfound", elt);
+		logger.log(Level.CONFIG, "{0} not found", elt);
+	    }
+	    if ('/' != File.separatorChar) {
+		path = path.replace('/', File.separatorChar);
+	    }
+	    addJar(path, jflist, jfmap);
+	}
+    }
+
+    /** Just keep looping, spawning a new thread for each incoming request. */
+    public void run() {
+	logger.log(Level.INFO, "ClassServer started [{0}, port {1}]",
+		   new Object[]{Arrays.asList(dirs),
+				Integer.toString(getPort())});
+	try {
+	    while (!isInterrupted()) {
+                try {
+                    new Task(server.accept()).start();
+                } catch (SocketTimeoutException e){
+                    // This happens every 5 minutes, it allows ClassServer to
+                    // be interrupted if necessary.
+                } catch ( SecurityException e){
+                    logger.log(Level.SEVERE, "Permission denied: ", e);
+                    interrupt();
+                }
+	    }
+	} catch (IOException e) {
+            if (verbose) {
+                e.printStackTrace();
+            }
+            if (!server.isClosed())
+                logger.log(Level.SEVERE, "accepting connection", e);
+        } finally {
+            terminate();
+        }
+    }
+
+    /** Close the server socket, causing the thread to terminate. */
+    public synchronized void terminate() {
+	verbose = false;
+	try {
+	    server.close();
+	} catch (IOException e) {
+	}
+	if (lifeCycle != null)
+	    lifeCycle.unregister(this);
+	logger.log(Level.INFO, "ClassServer terminated [port {0}]",
+		   Integer.toString(getPort()));
+    }
+
+    /** Returns the port on which this server is listening. */
+    public int getPort() {
+	return server.getLocalPort();
+    }
+
+    /** Read up to CRLF, return false if EOF */
+    private static boolean readLine(InputStream in, StringBuffer buf)
+	throws IOException
+    {
+	while (true) {
+	    int c = in.read();
+	    if (c < 0)
+		return buf.length() > 0;
+	    if (c == '\r') {
+		in.mark(1);
+		c = in.read();
+		if (c != '\n')
+		    in.reset();
+		return true;
+	    }
+	    if (c == '\n')
+		return true;
+	    buf.append((char) c);
+	}
+    }
+
+    /** Parse % HEX HEX from s starting at i */
+    private static char decode(String s, int i) {
+	return (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);
+    }
+
+    /** Decode escape sequences */
+    private static String decode(String path) {
+	try {
+	    for (int i = path.indexOf('%');
+		 i >= 0;
+		 i = path.indexOf('%', i + 1))
+	    {
+		char c = decode(path, i);
+		int n = 3;
+		if ((c & 0x80) != 0) {
+		    switch (c >> 4) {
+		    case 0xC:
+		    case 0xD:
+			n = 6;
+			c = (char)(((c & 0x1F) << 6) |
+				   (decode(path, i + 3) & 0x3F));
+			break;
+		    case 0xE:
+			n = 9;
+			c = (char)(((c & 0x0f) << 12) |
+				   ((decode(path, i + 3) & 0x3F) << 6) |
+				   (decode(path, i + 6) & 0x3F));
+			break;
+		    default:
+			return null;
+		    }
+		}
+		path = path.substring(0, i) + c + path.substring(i + n);
+	    }
+	} catch (Exception e) {
+	    return null;
+	}
+	return path;
+    }
+
+    /** Read the request/response and return the initial line. */
+    private static String getInput(Socket sock, boolean isRequest)
+	throws IOException
+    {
+	BufferedInputStream in =
+	    new BufferedInputStream(sock.getInputStream(), 256);
+	StringBuffer buf = new StringBuffer(80);
+	do {
+	    if (!readLine(in, buf))
+		return null;
+	} while (isRequest && buf.length() == 0);
+	String initial = buf.toString();
+	do {
+	    buf.setLength(0);
+	} while (readLine(in, buf) && buf.length() > 0);
+	return initial;
+    }
+
+    /** Simple daemon task thread */
+    private class Task extends Thread {
+	/** Socket for the incoming request */
+	private Socket sock;
+
+	/** Simple constructor */
+	public Task(Socket sock) {
+	    this.sock = sock;
+	    setDaemon(true);
+	}
+
+	/** Read specified number of bytes and always close the stream. */
+	private byte[] getBytes(InputStream in, long length)
+	    throws IOException
+	{
+	    DataInputStream din = new DataInputStream(in);
+	    byte[] bytes = new byte[(int)length];
+	    try {
+		din.readFully(bytes);
+	    } finally {
+		din.close();
+	    }
+	    return bytes;
+	}
+
+	/** Canonicalize the path */
+	private String canon(String path) {
+	    if (path.regionMatches(true, 0, "http://", 0, 7)) {
+		int i = path.indexOf('/', 7);
+		if (i < 0)
+		    path = "/";
+		else
+		    path = path.substring(i);
+	    }
+	    path = decode(path);
+	    if (path == null || path.length() == 0 || path.charAt(0) != '/')
+		return null;
+	    return path.substring(1);
+	}
+
+	/** Return the bytes of the requested file, or null if not found. */
+	private byte[] getBytes(String path) throws IOException {
+	    if (map != null) {
+		int i = path.indexOf('/');
+		if (i > 0) {
+		    JarFile[] jfs = (JarFile[])map.get(path.substring(0, i));
+		    if (jfs != null) {
+			String jpath = path.substring(i + 1);
+			for (i = 0; i < jfs.length; i++) {
+			    JarEntry je = jfs[i].getJarEntry(jpath);
+			    if (je != null)
+				return getBytes(jfs[i].getInputStream(je),
+						je.getSize());
+			}
+		    }
+		}
+	    }
+	    if ('/' != File.separatorChar) {
+		path = path.replace('/', File.separatorChar);
+	    }
+	    for (int i = 0; i < dirs.length; i++) {
+		File f = new File(dirs[i] + path);
+		if (perms[i].implies(new FilePermission(f.getPath(), "read")))
+		{
+		    try {
+			return getBytes(new FileInputStream(f), f.length());
+		    } catch (FileNotFoundException e) {
+		    }
+		}
+	    }
+	    return null;
+	}
+
+	/** Process the request */
+	public void run() {
+	    try {
+		DataOutputStream out =
+		    new DataOutputStream(sock.getOutputStream());
+		String req;
+		try {
+		    req = getInput(sock, true);
+		} catch (Exception e) {
+		    if (verbose) {
+			print("classserver.inputerror",
+			      new String[]{sock.getInetAddress().getHostName(),
+					   Integer.toString(sock.getPort())});
+			e.printStackTrace();
+		    }
+		    logger.log(Levels.HANDLED, "reading request", e);
+		    return;
+		}
+		if (req == null)
+		    return;
+		if (req.startsWith("SHUTDOWN *")) {
+		    if (verbose)
+			print("classserver.shutdown",
+			      new String[]{sock.getInetAddress().getHostName(),
+					   Integer.toString(sock.getPort())});
+		    boolean ok = stoppable;
+		    try {
+			new ServerSocket(0, 1, sock.getInetAddress());
+		    } catch (IOException e) {
+			ok = false;
+		    }
+		    if (!ok) {
+			out.writeBytes("HTTP/1.0 403 Forbidden\r\n\r\n");
+			out.flush();
+			return;
+		    }
+		    try {
+			out.writeBytes("HTTP/1.0 200 OK\r\n\r\n");
+			out.flush();
+		    } catch (Exception e) {
+			if (verbose)
+			    e.printStackTrace();
+			logger.log(Levels.HANDLED, "writing response", e);
+		    }
+		    terminate();
+		    return;
+		}
+		String[] args = null;
+		if (verbose || logger.isLoggable(Level.FINE))
+		    args = new String[]{req,
+					sock.getInetAddress().getHostName(),
+					Integer.toString(sock.getPort())};
+		boolean get = req.startsWith("GET ");
+		if (!get && !req.startsWith("HEAD ")) {
+		    if (verbose)
+			print("classserver.badrequest", args);
+		    logger.log(Level.FINE,
+			       "bad request \"{0}\" from {1}:{2}", args);
+		    out.writeBytes("HTTP/1.0 400 Bad Request\r\n\r\n");
+		    out.flush();
+		    return;
+		}
+		String path = req.substring(get ? 4 : 5);
+		int i = path.indexOf(' ');
+		if (i > 0)
+		    path = path.substring(0, i);
+		path = canon(path);
+		if (path == null) {
+		    if (verbose)
+			print("classserver.badrequest", args);
+		    logger.log(Level.FINE,
+			       "bad request \"{0}\" from {1}:{2}", args);
+		    out.writeBytes("HTTP/1.0 400 Bad Request\r\n\r\n");
+		    out.flush();
+		    return;
+		}
+		if (args != null)
+		    args[0] = path;
+		if (verbose) {
+		    print(get ? "classserver.request" : "classserver.probe",
+			  args);
+		}
+		logger.log(Level.FINER,
+			   get ?
+			   "{0} requested from {1}:{2}" :
+			   "{0} probed from {1}:{2}",
+			   args);
+		byte[] bytes;
+		try {
+		    bytes = getBytes(path);
+		} catch (Exception e) {
+		    if (verbose)
+			e.printStackTrace();
+		    logger.log(Level.WARNING, "getting bytes", e);
+		    out.writeBytes("HTTP/1.0 500 Internal Error\r\n\r\n");
+		    out.flush();
+		    return;
+		}
+		if (bytes == null) {
+		    if (verbose)
+			print("classserver.notfound", path);
+		    logger.log(Level.FINE, "{0} not found", path);
+		    out.writeBytes("HTTP/1.0 404 Not Found\r\n\r\n");
+		    out.flush();
+		    return;
+		}
+		out.writeBytes("HTTP/1.0 200 OK\r\n");
+		out.writeBytes("Content-Length: " + bytes.length + "\r\n");
+		out.writeBytes("Content-Type: application/java\r\n\r\n");
+		if (get)
+		    out.write(bytes);
+		out.flush();
+		if (get)
+		    fileDownloaded(path, sock.getInetAddress());
+	    } catch (Exception e) {
+		if (verbose)
+		    e.printStackTrace();
+		logger.log(Levels.HANDLED, "writing response", e);
+	    } finally {
+		try {
+		    sock.close();
+		} catch (IOException e) {
+		}
+	    }
+	}
+    }
+
+    private static ResourceBundle resources;
+    private static boolean resinit = false;
+
+    private static synchronized String getString(String key) {
+	if (!resinit) {
+	    resinit = true;
+	    try {
+		resources = ResourceBundle.getBundle("org.apache.river.tool.resources.classserver");
+	    } catch (MissingResourceException e) {
+		logger.log(Level.WARNING, "missing resource bundle {0}",
+			   "org.apache.river.tool.resources.classserver");
+	    }
+	}
+	if (resources != null) {
+	    try {
+		return resources.getString(key);
+	    } catch (MissingResourceException e) {
+	    }
+	}
+	return null;
+    }
+
+    private static void print(String key, String val) {
+	String fmt = getString(key);
+	if (fmt == null)
+	    fmt = "no text found: \"" + key + "\" {0}";
+	System.out.println(MessageFormat.format(fmt, new String[]{val}));
+    }
+
+    private static void print(String key, String[] vals) {
+	String fmt = getString(key);
+	if (fmt == null)
+	    fmt = "no text found: \"" + key + "\" {0} {1} {2}";
+	System.out.println(MessageFormat.format(fmt, vals));
+    }
+
+    /**
+     * This method provides a way for subclasses to be notified when a
+     * file has been completely downloaded.
+     * 
+     * @param fp The path to the file that was downloaded.
+     */
+    protected void fileDownloaded(String fp, InetAddress addr) {
+    }
+
+    /**
+     * Command line interface for creating an HTTP server.
+     * The command line options are:
+     * <pre>
+     * [-port <var>port</var>] [-dir <var>dirlist</var>] [-dirs <var>dirlist</var>] [-stoppable] [-verbose] [-trees]
+     * </pre>
+     * The default port is 8080; the default can be overridden with
+     * the <code>-port</code> option.  The default directory on Windows is
+     * <code>J:</code> and the default on other systems is
+     * <code>/vob/jive/lib-dl</code>; the default can be overridden with the
+     * <code>-dir</code> or <code>-dirs</code> option, providing one or more
+     * directories separated by the {@linkplain File#pathSeparatorChar
+     * path-separator character}. All files under these directories (including
+     * all subdirectories) are served up via HTTP.  If the pathname of a file
+     * is <var>path</var> relative to one of the top-level directories, then
+     * the file can be downloaded using the URL
+     * <pre>
+     * http://<var>host</var>:<var>port</var>/<var>path</var>
+     * </pre>
+     * If a relative <var>path</var> matches a file under more than one
+     * top-level directory, the file under the first top-level directory
+     * with a match is used. No caching of directory contents or file contents
+     * is performed. <p>
+     *
+     * If the <code>-stoppable</code> option is given, the HTTP server can be
+     * shut down with a custom HTTP <code>SHUTDOWN</code> request originating
+     * from the local host. The command line options for stopping an existing
+     * HTTP server are:
+     * <pre>
+     * [-port <var>port</var>] -stop
+     * </pre>
+     * <p>
+     *
+     * If the <code>-verbose</code> option is given, then all attempts to
+     * download files are output. <p>
+     * 
+     * The <code>-trees</code> option can be used to serve up individual files
+     * stored within JAR and zip files in addition to the files that are
+     * served up as described above. If the option is used, the server finds
+     * all JAR and zip files in the top-level directories (not in
+     * subdirectories).  If the name of the JAR or zip file is
+     * <var>name</var><code>.jar</code> or <var>name</var><code>.zip</code>,
+     * then any individual file named <var>file</var> within it (or within the
+     * JAR or zip files referenced transitively in <code>Class-Path</code>
+     * manifest attributes), can be downloaded using a URL of the form:
+     * <pre>
+     * http://<var>host</var>:<var>port</var>/<var>name</var>/<var>file</var>
+     * </pre>
+     * If multiple top-level directories have JAR or zip files with the same
+     * <var>name</var>, the file under the first top-level directory with a
+     * match is used. If a <code>Class-Path</code> element matches a file under
+     * more than one top-level directory, the file under the first top-level
+     * directory with a match is used. When this option is used, an open file
+     * descriptor and cached information is held for each JAR or zip file, for
+     * the life of the process.
+     */
+    public static void main(String[] args) {
+	int port = DEFAULT_PORT;
+	String dirlist = DEFAULT_DIR;
+	if (File.separatorChar == '\\')
+	    dirlist = DEFAULT_WIN_DIR;
+	boolean trees = false;
+	boolean verbose = false;
+	boolean stoppable = false;
+	boolean stop = false;
+	for (int i = 0; i < args.length ; i++ ) {
+	    String arg = args[i];
+	    if (arg.equals("-port")) {
+		i++;
+		port = Integer.parseInt(args[i]);
+	    } else if (arg.equals("-dir") || arg.equals("-dirs")) {
+		i++;
+		dirlist = args[i];
+	    } else if (arg.equals("-verbose")) {
+		verbose = true;
+	    } else if (arg.equals("-trees")) {
+		trees = true;
+	    } else if (arg.equals("-stoppable")) {
+		stoppable = true;
+	    } else if (arg.equals("-stop")) {
+		stop = true;
+	    } else {
+		print("classserver.usage", (String)null);
+		return;
+	    }
+	}
+	try {
+	    if (stop) {
+		Socket sock = new Socket(LocalHostLookup.getLocalHost(), port);
+		try {
+		    DataOutputStream out =
+			new DataOutputStream(sock.getOutputStream());
+		    out.writeBytes("SHUTDOWN *\r\n\r\n");
+		    out.flush();
+		    String status = getInput(sock, false);
+		    if (status != null && status.startsWith("HTTP/")) {
+			status = status.substring(status.indexOf(' ') + 1);
+			if (status.startsWith("403 ")) {
+			    print("classserver.forbidden", status);
+			} else if (!status.startsWith("200 ") &&
+				   status.indexOf(' ') == 3)
+			{
+			    print("classserver.status",
+				  new String[]{status.substring(0, 3),
+					       status.substring(4)});
+			}
+		    }
+		} finally {
+		    try {
+			sock.close();
+		    } catch (IOException e) {
+		    }
+		}
+	    } else {
+		new ClassServer(port, dirlist, trees, verbose,
+				stoppable).start();
+	    }
+	} catch (IOException e) {
+	    logger.log(Level.WARNING, "requesting shutdown", e);
+	}
+    }
+}

Modified: river/jtsk/modules/modularize/apache-river/tools/pom.xml
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/pom.xml?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/pom.xml (original)
+++ river/jtsk/modules/modularize/apache-river/tools/pom.xml Sun Jul  5 11:41:39 2020
@@ -41,6 +41,8 @@
         <module>jarwrapper</module>
         <module>preferredlistgen</module>
         <module>security-policy-debug</module>
+	<!--
         <module>policy-condenser</module>
+	-->
     </modules>
 </project>

Modified: river/jtsk/modules/modularize/apache-river/tools/preferredlistgen/pom.xml
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/tools/preferredlistgen/pom.xml?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/tools/preferredlistgen/pom.xml (original)
+++ river/jtsk/modules/modularize/apache-river/tools/preferredlistgen/pom.xml Sun Jul  5 11:41:39 2020
@@ -20,7 +20,8 @@
     <parent>
         <groupId>org.apache.river</groupId>
         <artifactId>tools</artifactId>
-        <version>3.1.1-SNAPSHOT</version>
+        <version>3.0-SNAPSHOT</version>
+	<relativePath>../</relativePath>
     </parent>
 
     <groupId>org.apache.river.tools</groupId>