aka Miniature JWS
(with Android and SSL support)
Truth is ever to be found in simplicity, and not in the multiplicity and confusion of things.
The Miniature Java Web Server is built as a servlet container with HTTPD servlet providing the standard Web server functionality. The server is a small as in Java code as in result footprint. This is a general purpose web server and servlet container running on a wide specter of hardware and software where Java is supported. Any application where a size of a server and a performance is critical. The server is a reasonably scalable and can serve up to thousand concurrent requests. I found also very convenient shipping a servlet based product packaging with the server, so a user can start a product just after unwrapping. Android usage of the server is a very beneficial and I use it for several Android running servlets which work better than native Android applications. (Play Store)
TestimoniesUsing tjws with WebServices as a "RMI"/RPC protocol, and with
HTTPS on I've downloaded TJWS and it looks like exactly what I"e;ve been looking for to use as a local web server to generate a web application. I'm using the miniature java webserver on TJWS was the only thing out there I found I could use as a library, programmatically within unit tests. Thanks for a great library. |
Features and Benefits
HistoryI was looking for a web server with sources to debug some servlets at the end of 1998. One of my findings was ACME Java Web Server. It was pretty good, but supported only JSS 1.x and JDK 1.02. Since my servlets required a bit more, I have added support for contemporary versions of JDK and JSDK. I just continued adding more features and providing bug fixes after. The current version is mostly compatible with the latest servlet container specification (4.0) and offers also the websocket support. Servlet technology todayOracle stopped the servlet support couple years ago and gave rights to continue working on the technology to Jakata group. It's certainly not a good news for TJWS. Jakarta renamed all servlet packages to avoid a clash with core Java technologies. Since TJWS considers JSS version 4.0 as sufficient for most applications, it didn't rename the packages yet. Probably in the future the packages will be renamed especially if Jakarta group issued any interest in TJWS. |
Main competitors for TJWS are Winstone, Eclipse Jetty. TJWS can successfully compete with more established and reputable servers as Tomcat, Glassfish, and JBOSS. Main benefits of TJWS are simplicity and a small footprint.
Modular design is used for TJWS. It gives a flexibility of creation different configurations with exactly required features. A heart of TJWS is a light weight servlet container. A set of predefined servlets extends functionality of the container transferring it to a web server or/and an application server. Predefined servlets can be eliminated or redefined for extra flexibility.
Selecting or not J2EE deployment gives an extra flexibility in the final size of a deployed application. Only Jasper is currently supported as JSP provider. However I separate it to avoid any licensing issues. Core server and J2EE deployment modules have sizes 119K, and 98K correspondingly. Jasper's size is about 1.5M. (I am still looking for help to separate Jasper on runtime and JSP parsing and compilation parts, it could be beneficial for Android deployment.) The app server services module adds 52Kb. And finally websocket module adds 68Kb
Most of server configuration is based on command line arguments. The arguments can be processed from a file as well. Additional configuration values can be provided over files. J2EE deployed application are configured based on standard web.xml deployment descriptor. web.xml less deployment is also possible.
All command line parameters start with '-' (dash) and most of them have a following value part. Here they are:
p | specifies a port number served by TJWS, default value is 80 for non secure and 443 for a secure configuration, for example -p 8080 | ||||||||||||||||
t | specifies a name of the throttles definition file. It allows to reduce
speed accessing to particular files and improve overall performance of the server.
An example of this file can look like:
|
||||||||||||||||
s | specifies a servlet properties file in old Sun's legacy form. An example of
this file can look like:
servlet. is just a keyword. Next part is a servlet URL mapping and a
servlet name. A mapping notation was changed since 1.93 to match the servlet specification,
so /* should be added to the old notation. Next part is dot (.) separator of servlet class name, when code used,
or init arguments comma separated name=value pairs when initArgs is used. |
||||||||||||||||
r | specifies realm file. A format of this file looks like:
|
||||||||||||||||
a | specifies aliases definition file. Every line of the file
specifies one alias entry starting from keyword from=webpath;dir=directory_in_filesysten_to_map, for example:
|
||||||||||||||||
b | a bind address, if your machine has several IP addresses, then you can specify which one to use | ||||||||||||||||
k | a backlog size, by default 50, but can't be less than 2 | ||||||||||||||||
nohup | server doesn't expect any terminal window, can be only killed to stop | ||||||||||||||||
c | specifies a web path of CGI scripts directory | ||||||||||||||||
e | provides session timeout in minutes and can be a negative. Negative value won't start session cleaning thread and will use a persistent session cookie | ||||||||||||||||
m | limits max number of active sessions by a specified value, can't be less than 10. Default value is no limitation | ||||||||||||||||
l[ar][f format_string] | specifies to log accesses with optionally logged user-agent and
referer HTTP headers. When f modifier specified a format string
has to follow which is a valid format string for java.text.MessageFormat class.
Positions of parameters are:
|
||||||||||||||||
g | requests rolling log file after reaching parameter specified line numbers. Can't be made less than 1000, and no rolling used if not specified or parameter is 0. Since 1.102 | ||||||||||||||||
w | provides web app deployer class name, by default used rogatkin.web.WarRoller | ||||||||||||||||
j | JSP servlet class, org.gjt.jsp.JSPServlet is default; extra init parameters can be specified for JSP servlet; syntax of parameters is -class_name_perfix.param_name param_value; note that value can contain variables like %context% and %deploydir% substituted by actual values respectfully. Another substitution happens for %classloader% by a name of a servlet context attribute keeping an instance of a class loader used for a web application deployment (available from version 1.15). Version 1.22 and above introduced another variable %classpath% which substituted by class path used for loading servlet | ||||||||||||||||
nka | no keep alive (server uses keep-alive by default) | ||||||||||||||||
kat | keep alive timeout interval in seconds, default is 30 | ||||||||||||||||
mka | max number of a connection use in keep-alive | ||||||||||||||||
sh | HTTP only attribute for session cookie. Can improve a security. A session cookie doesn't carry this attribute by default. Since 1.90 | ||||||||||||||||
ss | Secure only attribute for session cookie. Can improve a security. A session cookie doesn't carry this attribute by default. Since 1.99 | ||||||||||||||||
sp | persistence for sessions, TJWS is capable storing sessions data in a portable format and reload them between restarts or nodes of a cluster. Do not use this option if sessions contain sensitive, not serializable, or bulky data | ||||||||||||||||
err | allows to use own or standard error print stream. If there is no following parameter class name then used System.err as error print stream. If a parameter specified, then it's considered as a class name compatible for assignment to PrintStream. Such class will be instantiated and used for error redirection | ||||||||||||||||
out | allows to define own class which will handle log needs. This class has to be assignment compatible with PrintStream. An attempt of instantiation of the class with default constructor happens at TJWS startup, so the class has to be available in startup class path. This class will handle err stream too unless -err option is specified. TJWS includes class Acme.Utils$DummyPrintStream for disabling any log printing. (since 1.26) | ||||||||||||||||
d | Log file directory for default logging, System.getProperty("user.dir") is used by default (since 1.30) | ||||||||||||||||
z | defines a max number of created threads in the thread pool used for servicing requests. 20 used when the parameter is not defined or invalid. 0 allows to grow the pool endlessly, -1 means to create a thread for every request (it's useful for virtual threads (Java 21 or above is required), (since 1.120) | ||||||||||||||||
socketFactory | specifies class name of socket factory and used for setting
a secure connection. (deprecated since 1.30)
It accepts also any freely specified options in form -option_name option_value. Such options passed without checking to a custom server socket factory implementation and other modules of the server |
||||||||||||||||
acceptorImpl | specifies a class name of a concerete Acceptor implementation. Default is Acme.Serve.SimpleAcceptor (since 1.31). See note above about processing additional connection parameters. Since 1.30 | ||||||||||||||||
dataSource | specifies a data source properties file location. Supported by rogatkin.app.Main
run module. Since 1.30 This option is valid only for app server runner (class rogatkin.app.Main) TJWS .war deployer can process also context.xml file supplied in META-INF directory of application .war for same purpose. Since 1.98. A property file can contain the following properties:
|
TJWS processes several Java System level definitions in addition to command line arguments specified as JVM's -D arguments:
tjws.serve.log.encoding - this definition specifies encoding used for log messages, it can be very convenient to debug multi lingual web applications.
tjws.proxy.ssl - this definition specifies that server should process X-Forwarded-xxxx headers for calculation remote and server addresses. If value of the property 'y', then SSL is considered to be handled by a proxy server. (Since 1.71)
java.protocol.handler.pkgs - this definition is used by SSL socket factories implementations to define a different protocol handler packages, than standard com.sun.net.ssl.internal.www.protocol
tjws.webappdir - specifies path to web application war files location for automatic deployment. By default TJWS_ROOT/webapps directory is used.
tjws.webclassloader - specifies custom class loader class name used for loading classes from war file. The class loader must to have constructor accepting parameters as URL[] and ClassLoader. Since 1.83
tjws.wardeploy.warname-as-context - see details below.
tjws.wardeploy.as-root[.virtual_host_name] - defines context name/war name used for deploying in root, e.g. -Dtjws.wardeploy.as-root=<app_context/war_name> If virtual host name part is presented, then it defines a fully qualified host name for which the root context is set. (Since 1.71)
tjws.virtual - defines deployment of web applications in virtual hosting environment.
When the definition is specified, TJWS J2EE web applications deployer looks in all subdirectories
under the automatic web application deployment directory and considers directory name as virtual
host name and directory content as automatic web application deployment directory for
corresponding virtual host.
For example:
TJWS_ROOT/webapps www.travelspal.com travelspal.war webfolder.war www.7bee.org sqlfair.war webchat.war xumster.war jaddressbook.war
class_name.debug - this definition is passed to JSP provider for allowing debug specified class name.
tjws.fileservlet.usecompression - this definition advises to compress text content response when a client can accept it. To make this suggestion per application, use tjws.webapp.<context_name>.compressresponse Since 1.31
tjws.fileservlet.suppressindex this definition advises the file servlet to do not show content of a directory when an index file can't be found. Since 1.96
tjws.wardeploy.dynamically - this definition advises J2EE deployer for monitoring .war files updates and redeploy corresponding applications without the server restart. Optional value of this definition specifies time interval in seconds between checks. The option is very useful for CI/CD process. Since 1.50
tjws.wardeploy.noincremental - instructs TJWS redeploy entire web application when newer version of .war detected. Default is incremental deployment overriding only older files and not touching added. Since 1.93
tjws.webapp.context name.init.timeout - specifies init timeout in seconds of corresponding web application by context name, use * if you need to define it for all contexts
tjws.webapp.context name.threadpoolsets - specifies core, max threads, and queue size a thread pool of corresponding web application by context name, use * if you need to define it for all contexts. Since 1.80
tjws.webapp.debug - value yes turns on additional debug print outs for J2EE deployed apps
tjws.app.orb.arguments - this definition can provide comma separated parameters used for ORB initialization. Since 1.50
tjws.app.main - name of main class started with offering app server services. TJWS supports not only web applications, any desktop Java application can get benefits of app server services as JNDI and container managed JDBC connections. Since 1.50
tjws.app.main.classpath - class path for main application class specified as definition tjws.app.main, unless the main class can be resolved from boot class path. Use comma (,) to separate classpath components. Since 1.50
tjws.app.main.striprightparam - specifies a position in command line arguments which have to be not passed to a main class defined in tjws.app.main. It gives flexibility of separate command line arguments used by launched application and app server services itself. Since 1.50
tjws.app.main.stripleftparam specifies a position to cut from left. See description of tjws.app.main.striprightparam. Since 1.50
tjws.websocket.container "true" value specifies that websocket end points can be deployed in scope of TJWS itself (not in scanning from a .war packaged web application) TJWS class path is used for such deployment. The option is useful for the embedded usage of TJWS. Since 1.111
Since TJWS has a long history it supports as a legacy way of deployment, a configuration
of servlets as a new .war (web.xml) based. The legacy deployment uses property
files, like servlets.properties and aliases.properties, and finally Java annotations. J2EE way is based
on web.xml and config.xml files. Websocket
extension deployment uses the modern annotated classes mechanism.
Security becomes more important nowadays, so I decided to add SSL support to TJWS. Thanks JSSE for making that fairly easy. Here some tips how to install SSL support for the server.
There are three SSL supporting socket factories packaged with TJWS - Acme.Serve.SSLServerSocketFactory , rogatkin.web.DoubleHeadSocketFactory (available from v 1.17), and rogatkin.wskt.SSLSelectorAcceptor (available from v 1.110). First is recommended to use with core TJWS, when second with J2EE deployment module since it provides supporting http and https at the same time. Third is required for a websocket configuration. 7Bee script contains examples of usage two factories. Command bee -Dsecure=true runs TJWS using SSLSelectorAcceptor, and bee -Ddoublehead=true runs DoubleHeadSocketFactory. Additional command line parameters can be specified with each factory like :
Note that some secure socket options will override options specified in a regular way.
You may also adding your own socket factory implementations. See the packaged socket factories as a reference implementation.
Starting from 1.30 Socket Factory concept was replaced by Acceptor. It allowed to use Selector based processing requests with 10% improved performance and required for websocket. Five concrete Acceptor implementations are available:
The author appreciates if you can share own implementations of Acceptor.
For J2EE deployment you need to make sure that war.jar is specified in classpath when you start the server. It will create webapps directory (configured location) where you can put your .war files for auto deployment. Deployment gets updated at startup absorbing any changes from source .war file, however all changes done in target deployment directory are preserved. TJWS can monitor also source .war changes during runtime when tjws.wardeploy.dynamically is specified, and redeploy application if changes were detected. server.xml isn't supported and most of server specific parameters have to be specified as command line arguments, or stored in a flat file as cmdparams. All examples of startup scripts are presented in directory bin of a distributive archive. Most of examples contain both ways of server configuration and application deployment. Note that deployment descriptor (web.xml) parameter display-name defines a context path of a deployed web application. If you want to have a context path matching to .war name then add system property tjws.wardeploy.warname-as-context set to yes. For command line it will look like -Dtjws.wardeploy.warname-as-context=yes . (Since 1.24) To prevent application update at startup time you need to remove corresponding .war from the deployment directory. It gives also a way to deploy web applications without .war just manually create web app directory structure. Check section 'Embeddable application' for more options of a deployment and a distribution of applications.
Supported web.xml deployment tags are:
TJWS includes app server services module. It takes some usable shape from version 1.50. There are two services offered:
For using these services app.jar has to be in class path, or/and used for starting the server. The bin directory includes an example of starting TJWS with app server services on. Data sources get configured from properties files specified as dataSource command line option. JNDI properties as context factory and JNDI URL get pre-populated as rogatkin.app.SimpleJndi and http://localhost:1221 correspondingly, unless they are defined as system properties. JNDI is capable to register local and CORBA objects. First running JNDI takes care of JNDI master repository, and all following JNDI starts will be registered in the master repository. If the master repository's gone, then all clients won't be capable to register own CORBA objects or access them, until the repository is back. There is no persistence for stored references, so you should do a defended programming and re-register references in case of crashing the master repository.
Data source definition can be specified in context.xml placed under META-INF directory of .war structure. (since 1.98)
Multiple URL patterns can be defined anywhere. Async and multi part features are supported. web.xml less deployment is possible.
You can use the Jasper JSP provider for servicing JSP pages inside an application. Since the original Jasper is a bit bulky for TJWS taste, it's recommended to strip it to a manageable size. TJWS distribution includes instructions how to modify, build, and connect Jasper in jasper.html of webroot directory Jasper of respectful Tomcat versions 5.x, 6.x (since 1.28), 7.x (since 1.83), 8.x, and 9.x since 1.119 are supported. Since Tomcat 10 and up requires Java 11 and up, and Jakarta naming got in the place, those versions will be unlikely supported.
TJWS prepackaged with the following main classes you can use to start the server.
You can run the server of the version (>1.42/1.7) as Windows service. File servservice.exe added to the distribution. I wrote this service for JDK 1.4. The service starter considers that all TJWS files reside in the same directory specified at installation of the service. .jar files can be in sub directory lib. Command line parameters have to be specified in cmdparams file. Use -nohup switch to avoid a console read attempt. To get help line, run servservice.exe -help. Parameterless version of servservice.exe is considered as a service.
Note that arg[0] which supposes to give a fully qualified name of a service
executable on some versions Windows ((like XP) doesn't do that. For this reason
you have to specify a fully qualified path as the last parameter of an
installation command.
There is no requirements to have servservice.exe in the same
directory where TJWS is. Here is an example of an installation command:
servservice.exe -install "C:\Project Files\tjws" "C:\Project Files\gnujsp\lib\jspengine.jar" TinyJavaWebServer TinyJavaWebServer "C:\Project Files\tjws\servservice\Debug\servservice.exe" -Dtjws.wardeploy.warname-as-context=yes
Since I do not have a Windows machine in my possession currently, I didn't update this paragraph recently.
But I believe you can do required updates as needed, specifically add more required jars in a classpath.
A Linux service script example tjwserv is provided in the bin directory of the distribution archive. It has to be edited to reflect particular TJWS installation directory structure. The script has to be stored in /etc/init.d/ location. Use command update-rc.d tjwserv defaults to enable the service. You can control it using command service tjwserv <start|stop|restart>. Look in the Raspberry PI setup section for enabling service on Arch Linux and other systems using systemd. 1.x/doc/sbc directory has also good instructions cover Linux and FreeBSD platforms.
Recently, a new type of application appeared on the market. After starting an
application is launching a browser which represents its UI. This approach has many advantages and becomes more
popular and a wide used. The
Miniature Java Web Server is a right tool for creation such kind of application.
Download
and double click JAR in Explorer or launch it from a terminal typing java -jar
finesearch.jar,
then point browser to http://localhost:8080/finesearch
and enjoy the web interfaced application. Starting from version
1.21 TJWS includes a launcher of a .war packaged application from command line or a
start script. The feature is very similar to used by Winstone. Use:
java -jar webapplauncher.jar war_file_name [optional standard TJWS CLI
parameters]
for example:
java -jar webapplauncher.jar "C:\Project Files\finesearch\finesearch_app\finesearch.war"
Note if extra command line arguments are not specified, then TJWS will try to
discover them from cmdparams located in a working directory.
Version 1.22 and above makes launching Web UI application even more simpler. Web
application .war can be packaged inside of TJWS .jar file allowing one click
launch. To package web application .war with TJWS use target 'embedded' of
7Bee
build tool and answer on few simple questions. For example:
C:\Project Files\tjws>bee
embedded Launcher's been built. Enter command line arguments for app [-nohup -p 80]? -p 80 Enter application .war file location? C:\Project Files\finesearch\finesearch_app\finesearch.war |
TJWS gives a good possibility to create enterprise class J2EE applications without an expensive and a heavy weight application server. WebBee library will take care of SOA registry, MVC servlet framework with template based presentation layer, data persistence and much more. New generation of web application building blocks WebBee with annotated JDO and forms makes creation of a rich application possible even for Android platform.
TJWS can be successfully used as a part of another Java application. Acme.Serve.Serve
can be instantiated as a Java bean with following setting parameters in its public member
arguments and log print stream in its public member logStream.
Use method addServlet(..) for adding servlets. Note that
server will do nothing without servlets. Default file (HTTPD) and cgi servlets can be added
calling addDefaultServlets(...). Server can be started calling
method serve() and stopped calling notifyStop().
Note that serve() doesn't exit until a server runs, so stopping should be called
from a separate thread, or serve() is ran in a separate thread.
A minimal application with embedded TJWS looks like:
public class Test { public static void main(String... args) { class MyServ extends Acme.Serve.Serve { // Overriding method for public access public void setMappingTable(PathTreeDictionary mappingtable) { super.setMappingTable(mappingtable); } // add the method below when .war deployment is needed public void addWarDeployer(String deployerFactory, String throttles) { super.addWarDeployer(deployerFactory, throttles); } public void addWebsocketProvider() { // add if plan to deploy websocket endpoints addWebsocketProvider(null); // list of class path file components can be provided here } }; final MyServ srv = new MyServ(); // setting aliases, for an optional file servlet Acme.Serve.Serve.PathTreeDictionary aliases = new Acme.Serve.Serve.PathTreeDictionary(); aliases.put("/*", new java.io.File("C:\\temp")); // note cast name will depend on the class name, since it is anonymous class srv.setMappingTable(aliases); // setting properties for the server, and exchangeable Acceptors java.util.Properties properties = new java.util.Properties(); properties.put("port", 80); properties.setProperty(Acme.Serve.Serve.ARG_NOHUP, "nohup"); properties.setProperty("acceptorImpl", "Acme.Serve.SelectorAcceptor"); // this acceptor is requireed for websocket support srv.arguments = properties; srv.addDefaultServlets(null); // optional file servlet srv.addWebsocketProvider(); // enable websocket srv.addServlet("/myservlet", new MyServlet()); // optional // the pattern above is exact match, use /myservlet/* for mapping any path startting with /myservlet (Since 1.93) Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { srv.notifyStop(); srv.destroyAllServlets(); } })); srv.serve(); } }
The File servlet without aliases definitions maps your file system directly to a web accessible one, so setting up aliases is recommended.
J2EE servlet deployment is possible in the embedded usage. You need to assure
that war.jar is in a class path of an
application.
Add a line as below:
((Test$1)srv).addWarDeployer(null, null);
The default war deployer will look in directory "user.dir/webapps" and deploy all wars there. You can redefine a deploy repository by setting
System.setProperty("tjws.webappdir", newDeployDirectory);
prior of calling addWarDeployer()
TJWS works perfectly on Android platform as an embedded server enriching your Android application by a capability to receive uploads, check Kamerton application using TJWS for uploading music to Android device.
TJWS is a naturally created for running web applications on Raspberry Pi. It will outperform most of other application servers on the platform including Jetty, JBOSS and Tomcat. Start time for it is just around 11 seconds, when you can observe times close to 1 minute with other application servers. I created another document helping to run TJWS on SBC.
TJWS can be used with proxy servers. Definition tjws.proxy.ssl
can be specified to correctly determinate remote host and access protocol.
To use Apache as a proxy server you can:
ProxyRequests On ProxyVia On ProxyPass /context_path/ http://tjws_host:<TJWS port>/web_app_context/ ProxyPassReverse /context_path/ http://tjws_host:<TJWS port>/web_app_context/ # for pushing authentication RewriteEngine on RewriteBase / RewriteCond %{REMOTE_USER} !="" RewriteRule .* - [E=E_USER:%{REMOTE_USER}] RequestHeader set my_new_header %{E_USER}e
<VirtualHost *:*> ProxyPreserveHost On ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/ ServerName www.tjws.com </VirtualHost>To use Nginx as a proxy server :
server { listen 80; server_name localhost; ... location / { proxy_pass http://localhost:8080; proxy_set_header x-Forwarded-for $remote_addr; proxy_set_header X-Forwarded-Host $host; } ...Please note that Nginx doesn't support the keep alive for proxy requests yet, so you can observe some performance degradation.
Please download (version 1.121)
Note: Java 7 or better is required to build the core server. Java 8 is required for the rest. Java 21 or better is required for the virtual thread support. Specify env.xml variable 'android' as yes to compile the server to run on Android devices (since 1.83). App servers features are not supported for Android though. Since a build tool is a matter of personal taste, you can use any other favorite build tools.
Attention POSIX systems users, the distributive archive has access attribute
not set, so execute chmod -R +rwx WebServer
after unpackaging WebServer-nnn.zip
Any web application not using EJB will work on the server.
TJWS uses Java build tool 7Bee. The tool is available in sources at GitHub. Or download a pre-built version. There is also no way to convert .war files to be deployed on Android, than using 7Bee. The following values needed to be provided in env.xml to build TJWS:
TJWS was tested with most popular JVMs under Windows, Linux, FreeBSD, Mac OS, and Android platforms.
The Miniature Java Web Server carries all copyrights of the original author as stated in the license you can find in any source file.
The Tiny Java Web Server inherited BSD like license from the original code, check any source file for details.
I provide support of the server on voluntary basis. Feel free to send bug report on enhancement request. I also provide consulting service related to creation of web 2.0 J2EE scalable applications.
Feel free also to share your concerns, questions, and discoveries in the Discussion forum.
Version of 1.07 and later includes some useful web applications packaged as .war files and deployed at first server run. To enjoy the applications just follow a link on a start page. If you do not want to have these applications deployed, just remove corresponding .war files from webapps directory before first server run.
I'm looking for developers to finish work on pending web.xml, fragment.xml, and common.xml instructions. Another plan is adding SOAP/RPC support for easy SOA. SSI servlet is also waiting to be developed. There are tons opportunities to develop Android web applications.
Bugs, questions, and enhancement requests you can send to Dmitriy Rogatkin. Happy web servicing!