3 Development Embedding - ag88/embtomcatwebdav GitHub Wiki
This is released to maven central. https://central.sonatype.com/artifact/io.github.ag88/embtomcatwebdav/1.1.0
<dependency>
<groupId>io.github.ag88</groupId>
<artifactId>embtomcatwebdav</artifactId>
<version>1.1.0</version>
</dependency>
- v0.3.3 added runserverfork() method which lets apps embedding this to run the server in a standalone thread. By default, runserver() method blocks, apps embedding this can call runserverfork() instead.
- v0.3.4 fixed a bug related to race conditions in isRunning() method and runserverfork()
- v0.4.1 is a a rather major refactored release, this release is released to maven central
- v0.4.1 added a refactored command line options and config properties processing engine, this makes it feasible for apps linking the library to add command line options and config properties in the same app.
- v0.5.0 is a rather major feature release
- v0.5.0 added an Upload servlet that includes a form based file upload in the directory list. This makes it possible to upload files without a WebDAV client. In addition, it is styled with responsive html and css so that it is more readable on small (mobile) devices.
- v0.5.1 is a bugfix release for v0.5.0, Upload servlet, some refactoring: enabled alwaysUseSession for authentication, some mobile devices do not cache authentication and keeps prompting authentication every refresh and page. This is still as secure (managed by a session) and avoided the annoying auth prompts every screen. Login only at the start, and for cookie tests (needed for jesssion), only checks in doPost() where it is needed and only if it is a new (invalid) session.
- v0.5.2 added(fixed) sorting in Upload servlet
- v0.6.0 added access log
- v0.6.1 usability updates for upload servlet:
- limit authentication access to configured url prefix instead of /*, this helps to reduce authentication challenges for resources icons, e.g. favicon.ico etc for some browsers.
- added upload feedback on the page for large/long uploads, add upload feedback for the upload servlet, so that it shows 'uploading...' once upload is clicked.
- v0.6.2 usability updates for upload servlet:
- added a link to upload section at the top, this help with long directory lists relieves from long scrolls to the bottom just for uploads
- v0.8.0 major feature release:
- new upload servlet:
- migrated to Apache Velocity templates.
- added download of multiple files as zip
- added filename filters Download multiple files as zip and filename filters significantly improves usability of the upload servlet
- v0.8.1 add support for ip 0.0.0.0 listen on all interfaces *new
- host aliases will be added for all interfaces if 0.0.0.0 is specified as the host name
- v0.8.2 improvements, fixes for config file generation.
- improvements, fixes for config file generation config file generation now adds description entries and is sorted
- improved help documentation / description
- upload servlet is now default from this release
- v0.9.0 checks for updates from this repository
- checks for updates from this repository
- added option to enable/disable check for updates
- display server info: Embtomcatwebdav and version in page for upload servlet
- v0.9.1 adds allowlinking option
- allow linking (e.g. follow symbolic links, warn: links can go outside working dir/path, only works in upload servlet)
- v0.9.2 make filter case insensitive
- in prior release the filter in upload servlet is case sensitive, this is troublesome searching for files/patterns. This minor update makes it case insensitive, word searches matches regardless of case
- v1.0.0 major feature release new QR Code Gui, scan QR code and connect !
- scan the QR Code with a mobile phone and connect to upload servlet !
- Added a 'setup wizard' that helps to configure embtomcatwebdav and get it up and running easily
- Gui configuration panels for the various server configuration
- it now uses a standard configuration file so that you can simply launch it and start uploading / downloading
- v1.1.0 major feature release create dir/folder from web interface, urlprefix changed to /files instead of /webdav
- added createdir servlet, it is now possible to create a new dir/folder from the web interface
- the default urlprefix is now http://<host/ipaddr>:8080/files instead of /webdav. This makes it easier to remember the url
- improvements in the gui, initial run now makes a better selection of the host address, added a copy url to clipboard button
This app can be embedded, to see how this can be done, take a look at the unit tests in src/test/java. Basically, create a new WebDavServer object, setup all the instance variables as desired using the getters and setters methods and call runserver() or runserverfork() method.
v0.3.4, v0.3.3 added runserverfork() method which lets apps embedding this to run the server in a standalone thread.
By default, runserver() method blocks, apps embedding this can call runserverfork() instead.
runserverfork()
method returns a WebDAVServerThread
object. This is the thread the server is running on. You may want to keep it in an instance variable for shutdown later, e.g.
WebDavServer webdavserver = new WebDavServer();
// this is the path/directory/folder served
webdavserver.setPath(System.getProperty("user.dir"));
// this is the Tomcat work folder, this must be a valid directory
// if this is null it would create a folder [user.dir]/tomcat.port
// and use that as Tomcat's work folder
webdavserver.setBasedir(System.getProperty("user.dir"));
webdavserver.setHost("localhost");
webdavserver.setPort(8080);
webdavserver.setUser("user");
webdavserver.setPasswd("password");
webdavserver.setUrlprefix("/webdav");
// the above setup the WebDAV server running at http://user:password@localhost:8080/webdav
// note that runserverfork() actually start and run the server in its own thread
WebDAVServerThread thread = webdavserver.runserverfork();
... do other things
// shutdown the server, this is optional, you may omit this
// runserver() and runserverfork() sets up a shutdown hook that runs stopserver() if
// the process is interrupted. This is only needed if you want to shutdown the server
// from your app.
thread.getServer().stopserver();
thread.interrupt();
There are some race conditions as Tomcat takes some time to startup (possibly a few seconds). Hence, for more stable embedded operations, after calling runserverfork(), use e.g. Thread.sleep(2000), to pause for a while before doing other operations that interact with the server.
With version v0.3.4, currently additional servlets needs to be added after the server is running, i.e. after runserver() or runserverfork(). An example is as follows:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import io.github.ag88.embtomcatwebdav.WebDAVServerThread;
import io.github.ag88.embtomcatwebdav.WebDavServer;
public class App
{
public App() {
}
public void run(String[] args) {
WebDavServer webdavserver = new WebDavServer();
// this is the path/directory/folder served
webdavserver.setPath(System.getProperty("user.dir"));
// this is the Tomcat work folder, this must be a valid directory
// if this is null it would create a folder [user.dir]/tomcat.port
// and use that as Tomcat's work folder
webdavserver.setBasedir(System.getProperty("user.dir"));
webdavserver.setHost("localhost");
webdavserver.setPort(8080);
webdavserver.setUser("user");
webdavserver.setPasswd("pass");
webdavserver.setUrlprefix("/webdav");
// the above setup the WebDAV server running at http://user:pass@localhost:8080/webdav
Servlet myservlet = new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
try {
out.println("<html><head><title>hello world</title><head><body>");
out.println("<h1>hello world</h1>");
out.println("</body></html>");
out.flush();
} finally {
out.close();
}
}
};
try {
// note that runserverfork() actually start and run the server in its own thread
WebDAVServerThread thread = webdavserver.runserverfork();
Tomcat tomcat = thread.getServer().getTomcat();
Container container = tomcat.getHost().findChild(""); //get the root context
if(container instanceof Context) {
Context context = (Context) container;
//this 'hello' is the name of your servlet
Tomcat.addServlet(context, "hello", myservlet);
//the first parameter is the http://prefix where you want to patch
//your servlet, the 2nd is the name of the above servlet
String prefix = "/hello/*";
context.addServletMappingDecoded(prefix, "hello");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main( String[] args )
{
App app = new App();
app.run(args);
}
}
With the above codes, the wabdav servlet runs at http://localhost:8080/webdav, and your servlet runs at http://localhost:8080/hello , in the same app.
Note that in the interest of leaner binaries, WebDavServer drop the JSP container when setting up Tomcat. In addition, various Servlet API 3.0+ java annotations may not be scanned. Hence, mainly POJO java servlets are supported.
With version v0.4.1, a refactored command line options and config properties processing engine is added, this makes it feasible for apps linking the library to add command line options and config properties in the same app.
An example is as follows:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import io.github.ag88.embtomcatwebdav.WebDavServer;
import io.github.ag88.embtomcatwebdav.opt.OptFactory;
public class App {
public App() {
}
public void run(String[] args) {
io.github.ag88.embtomcatwebdav.App embtcapp = new io.github.ag88.embtomcatwebdav.App();
WebDavServer dav = OptFactory.getInstance().getWebDAVserv();
embtcapp.parseargs(args);
dav.loadparams(OptFactory.getInstance().getOpts());
dav.runserver();
}
public static void main(String[] args) {
App app = new App();
app.run(args);
}
}
With the above codes and say that your app is packaged with Maven Assembly Plugin , that the above is also the main entry point for your app. After building the assembled jar archive, you could possibly run e.g.
java -jar myapp-0.0.1-SNAPSHOT-jar-with-dependencies.jar -h
usage: myapp-0.0.1-SNAPSHOT
-b,--basedir <basedir> set basedir, a work folder for tomcat,
default [current working dir]/tomcat.port
-c,--conf <configfile> load properties config file
-D,--digest use digest authentication
--genconf <configfile> generate properties config file
--genpass dialog to generate digest password
-h,--help help
-H,--host <hostname> set host
-p,--port <port> set port
-P,--path <path> set path, default current working dir
-q,--quiet mute (most) logs
-R,--realm <realmname> set realm name, default 'Simple'
-S,--secure <keystore,passwd> enable SSL, you need to supply a keystore
file and keystore passwd, if passwd is
omitted it'd be prompted.
-u,--user <username> set user
-w,--passwd <password> set password, you may omit this, it would
prompt for it if -u is specified
-x,--urlprefix <urlprefix> set urlprefix, default /webdav
With a few lines of codes above, you get a full app as like the distributed release jars.
The important parts of the code are in the
public void run(String[] args) {
io.github.ag88.embtomcatwebdav.App embtcapp = new io.github.ag88.embtomcatwebdav.App();
WebDavServer dav = OptFactory.getInstance().getWebDAVserv();
embtcapp.parseargs(args);
dav.loadparams(OptFactory.getInstance().getOpts());
dav.runserver();
}
These few calls needs to be run in that order to get the WebDAV server started up properly, and with the parameters properly loaded.
The parseargs(String[] args) parses the command line using commons cli . If the -c configfile option is specified, the configfile is loaded as well using java Properties.
The data is registered with the singleton OptFactory object.
The OptFactory is a singleton object should be accessed with OptFactory.getInstance() method. This is only works properly if the main io.github.ag88.embtomcatwebdav.App object is used.
The Properties are available after the
parseargs(String[] args)
call.
The Properties are accessible in the
OptFactory instance.
In the above example, edit public void run(String[] args) {
as follows:
public void run(String[] args) {
io.github.ag88.embtomcatwebdav.App embtcapp = new io.github.ag88.embtomcatwebdav.App();
WebDavServer dav = OptFactory.getInstance().getWebDAVserv();
embtcapp.parseargs(args);
// this part is added
Properties properties = OptFactory.getInstance().getProperties();
if (properties != null) {
PrintWriter writer = new PrintWriter(new FilterOutputStream(System.out) {@Override
public void close() throws IOException {
//don't close System.out
}
});
properties.list(writer);
writer.flush();
writer.close();
}
//
dav.loadparams(OptFactory.getInstance().getOpts());
dav.runserver();
}
sample run
java -jar myapp-0.0.1-SNAPSHOT-jar-with-dependencies.jar -c configfile.ini
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.opt.OptFactory loadproperties
SEVERE: opt: path, invalid value: , replaced with: /path
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.opt.OptFactory loadproperties
SEVERE: opt: basedir, invalid value: , replaced with: /path/tomcat.8082
-- listing properties --
basedir=
path=
password=pass
keystorepasswd=
urlprefix=/webdav
port=8082
host=localhost
digest=true
realm=myrealm
quiet=false
keystorefile=
user=user
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.WebDavServer runserver
INFO: tomcat basedir: /path/tomcat.8082
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.WebDavServer runserver
INFO: serving path: /path
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.WebDavServer runserver
INFO: Realm name: myrealm
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.WebDavServer runserver
INFO: Auth method: DIGEST
Apr 01, 2023 8:36:05 PM io.github.ag88.embtomcatwebdav.WebDavServer runserver
INFO: Webdav servlet running at http://localhost:8082/webdav/
...
With version v0.4.1, a refactored command line options and config properties processing engine is added, this makes it feasible for apps linking the library to add command line options and config properties in the same app.
This section is an addition to the above How to access properties from the config file specified by the -c configfile option section. An example as follows:
Create a class that extends io.github.ag88.embtomcatwebdav.opt.Opt
.
import io.github.ag88.embtomcatwebdav.opt.Opt;
class MyOpt extends Opt {
public MyOpt() {
this.name = "myopt";
this.description = "set myopt";
this.defaultval = null;
this.opt = "M";
this.longopt = "myopt";
this.argname = "value";
// only 3 classes supported: String, Integer, Boolean
this.valclazz = String.class;
}
}
The option is identified by the name, the rest of the instance variables are inputs to Commons CLI to build an Option.
The run() method as like the previous section's example is modified as follows:
public void run(String[] args) {
io.github.ag88.embtomcatwebdav.App embtcapp = new io.github.ag88.embtomcatwebdav.App();
WebDavServer dav = OptFactory.getInstance().getWebDAVserv();
MyOpt myopt = new MyOpt();
OptFactory.getInstance().addOpt(myopt);
embtcapp.parseargs(args);
if (OptFactory.getInstance().getOpt("myopt").getValue() != null) {
System.out.println(String.format("opt: %s, value: %s", myopt.getName(),
OptFactory.getInstance().getOpt("myopt").getValue().toString()
));
}
dav.loadparams(OptFactory.getInstance().getOpts());
dav.runserver();
}
The key is make a new object of MyOpt
and register it with the OptFactory singleton object,
using the addOpt() method.
OptFactory
should be accessed with OptFactory.getInstance() method.
This is only works properly if the main io.github.ag88.embtomcatwebdav.App object is used.
Next call parseargs(String[] args) to parse the command line. If the -c configfile option is specified, the configfile is loaded as well using java Properties as well.
A sample run as is as follows
java -jar myapp-0.0.1-SNAPSHOT-jar-with-dependencies.jar -h
usage: myapp-0.0.1-SNAPSHOT
-b,--basedir <basedir> set basedir, a work folder for tomcat,
default [current working dir]/tomcat.port
...
-M,--myopt <value> set myopt
...
java -jar myapp-0.0.1-SNAPSHOT-jar-with-dependencies.jar -M hello_world
opt: myopt, value: hello_world
...
The evaluated command line parameter values or property values is accessible in the OptFactory singleton object,
using the getOpt() method, after parseargs(String[] args) call.
The key used for retreval is the name instance variable as defined in the Opt object.
e.g.
OptFactory.getInstance().getOpt("myopt").getValue().toString()