Tomcat: Change the Virtual hosts programmatically?
Asked Answered
B

5

10

Tomcat offers a build in "Virtual Hosting" Support: An Engine/Web-Application can be configured to be responsible for a list of Domains. These Domains have to be put into the server.xml/context.xml files with a special xml directive.

=> Is there any possibility to change the Tomcat Configuration (in general) and especially the "Virtual Hosts" of a Web-Application/Engine programmatically?

For example if a new user signs up, I have to add his domain to the list of "accepted virtual hosts/domains". The only way I currently think of is changing the xml files via a script and then restart Tomcat.

Is there any way to add them add runtime via some Java-Methods programmatically?

Thank you very much! Jan

Bumgarner answered 13/11, 2009 at 12:33 Comment(0)
R
8

Tomcat provides APIs to create new virtual host. To get access to the wrapper object needed for this, you need to implement a ContainerServlet. You can create virtual host like this,

    Context context = (Context) wrapper.getParent();
    Host currentHost = (Host) context.getParent();
    Engine engine = (Engine) currentHost.getParent();

    StandardHost host = new StandardHost();
    host.setAppBase(appBase);
    host.setName(domainName);

    engine.addChild(host);

You need to make sure appBase directory exist and you have to find ways to persist the new host to the server.xml or you lose the host on restart.

However, this approach rarely works. If your users run their own apps, you really want run separate instances of Tomcat so you can sandbox the apps better. e.g. One app running out of memory doesn't kill all other apps.

If you provide the app, you can just use one host (defaultHost). You can get the domain name from Host header and do whatever domain-specific stuff in your code.

Rashida answered 13/11, 2009 at 13:49 Comment(8)
Hello ZZ Coder, thanks for your answer. Thats vey interesting, never heard about this, thats brilliant to know that there are ways to access the Host and Engine in Tomcat.Bumgarner
@Coder, how to get wrapper objectDenudate
@Denudate You need to create a servlet implementing ContainerServlet, which has a setter setWrapper(). Tomcat will call your setter to set the wrapper. I haven't used this since Tomcat 5 so the mechanism may have changed.Rashida
@Coder, I implemented the ContainerServlet created appBase directory also since I'm testing this locally I added the domainName in my hosts file in /drivers/etc/hosts but when I run this and then put that domainName in browser it gives blank page. Am I missing something?Denudate
May I ask if this would also writes to server.xml physical file? Will the update be gone after a restart?Haematoxylin
@Denudate I have the same problemSpecialty
@SoheilRahsaz I was not successful in doing this, even after following Coder's suggestion. Let us know if you have any success.Denudate
@Denudate I did it and posted the result here.Specialty
S
3

You shouldn't change the server environment programmatically and there are no reliable and standard ways to do this. Best is to do and keep it all on the webapp side. To start, a Filter is perfectly suitable for this. Store the names somewhere in a database table or a properties file which you cache in the application scope. Check the HttpServletRequest#getRequestURI() (or the getServerName() if it is a subdomain instead of pathinfo) and do the forwarding task accordingly.

Hope this helps.

Selfseeking answered 13/11, 2009 at 12:55 Comment(1)
I think this is the best way to do this. Why is there a db mapping needed. Can we not simply map the web-app to sub-domain?Mcquillin
T
3

Use JMX

ArrayList serverList = MBeanServerFactory.findMBeanServer(null);
MBeanServer server = (MBeanServer) serverList.get(0);
Object[] params = { "org.apache.catalina.core.StandardHost", hostName };
String[] signature = { "java.lang.String", "java.lang.String" };
server.invoke(new ObjectName("Catalina:type=Engine"), "addChild", params, signature);

If needed, retrieve the host object and work with it:

ObjectName host = new ObjectName("Catalina:type=Host,host=" + hostName);
server.setAttribute(host, new Attribute("autoDeploy", false));
server.invoke(host, "start", null, null);
Tarpon answered 27/2, 2013 at 8:38 Comment(1)
Thanks for your post! Please do not use signatures/taglines in your posts. Your user box counts as your signature, and you can use your profile to post any information about yourself you like. FAQ on signatures/taglinesPalaeography
S
1

I would suggest you set your application to be the default virtual host in server.xml so your single virtual host can respond to requests addressed to any host name. Tomcat ships with the localhost application set as the default virtual host. So you can see how to do this by simply inspecting the server.xml file of a vanilla tomcat installation. You can programatically determine the host name the user sent the request to using the ServletRequest.getServerName() method.

Tomcat used to ship with a web application called "host-manager". Note: this is different than the "manager" web application that still comes with Tomcat. Host manager allowed for changing configuration or adding new virtual hosts on the fly without restarting the server. You could interact with the host-manager over HTTP (programmatically if desired). However, it had the unfortunate flaw of not committing its changes to server.xml so they were all lost on a web server restart. For whatever reason, starting with version 6, Tomcat no longer ships with the host-manager application. So it doesn't appear to be supported anymore.

Songful answered 13/11, 2009 at 12:55 Comment(0)
S
1

To sum up ZZ Coder answer which guided me a lot:

You have to create a servlet that implements ContainerServlet and override setWrapper method to get the org.apache.catalina.Wrapper object.

For doing that you have to have privileged="true" in your context.xml Context tag or it will throw an exception. Then you can use the Wrapper object and:

StandardContext context = (StandardContext) wrapper.getParent();
StandardHost currentHost = (StandardHost) context.getParent();
StandardEngine engine = (StandardEngine) currentHost.getParent();

StandardHost host = new StandardHost();
host.setAppBase(currentHost.getAppBase()); //in my case I created another instance of the same application
host.setDomain(currentHost.getDomain());
host.setAutoDeploy(false); // not restarting app whenever changes happen
host.setName("domain.com");
host.setThrowOnFailure(true);// tell it to throw an exception here if it fails to create the host
host.setDeployOnStartup(true);
host.setStartChildren(true);
host.setParent(engine); 
// you can add multiple aliases
host.addAlias(alias);

StandardContext ctx = new StandardContext();
ctx.setDocBase(context.getDocBase()); //again I reused my same application setting
ctx.setPath("");
if(currentHost.getWorkDir() != null)
{//create a working directory based on your new host's name
    ctx.setWorkDir(currentHost.getWorkDir().replace(currentHost.getName(), host.getName()));
}
ctx.setName(host.getDomain());

//some extra config that you can use
ctx.setUseHttpOnly(false);
ctx.setReloadable(false);
ctx.setXmlValidation(false);
ctx.setXmlNamespaceAware(false);
ctx.setCrossContext(false);
ctx.setParent(host);
// you have to have this or it will not work!!
ctx.addLifecycleListener(new ContextConfig()); 

//you can also create resources and add it to the context like so:
final ContextResource res = new ContextResource();
res.setName("name");
res.setAuth("Container");
res.setType("javax.sql.DataSource");
ctx.getNamingResources().addResource(res);

host.addChild(ctx);

engine.addChild(host);

You can add properties to your resource by calling res.setProperty("name", "value") Some properties that you can use are: initialSize,maxTotal,maxIdle,maxWaitMillis,removeAbandonedOnBorrow,removeAbandonedTimeout,validationQuery,timeBetweenEvictionRunsMillis,driverClassName,url,username,password.

Another exciting thing to is to get the host from the tomcat engine by calling engine.findChild(domain) and use stop(), start(), getStateName() and have your own Tomcat Admin panel!

Specialty answered 27/8, 2020 at 5:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.