If I start a jetty server from an external jar using java -jar , and then how can I add another java web application to that specific port that has already been started ? for example, this code :
public class Main {
private static Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) throws Exception {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
Server jettyServer = new Server(5701);
jettyServer.setHandler(context);
ServletHolder jerseyServlet = context.addServlet(
org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
jerseyServlet.setInitParameter(
"jersey.config.server.provider.classnames",
Calculator.class.getCanonicalName());
try {
jettyServer.start();
jettyServer.join();
} catch (Exception e){
logger.error("error during server starting",e);
jettyServer.stop();
jettyServer.destroy();
}
}
}
If I take the try/catch out, Would the application be added to the already existed 5701 jetty server?
your code starts its own Jetty server on port 5701, there's nothing about that code that will add to a different Jetty server.
Lets say you have ServerFoo on port 5701 already started and running in its own JVM.
You now have another Webapp you want to add to that ServerFoo instance.
You will start a new JVM, lets call it DeployerBar, to control/manipulate the ServerFoo instance.
To do this, you need to communicate with that ServerFoo instance and give it everything it needs to start the WebApp itself (all of the classes, the configuration, etc) first.
Then ServerFoo will need a custom ClassLoader to load these new classes and configuration that it just received, giving it the ability to start this new webapp.
If this is kinda what you are looking to do, consider instead modifying ServerFoo to use the Jetty DeploymentManager to monitor a common webapps directory.
Then your deployment process is just putting all of the files (classes/jars/libs/configuration) into this common webapps directory for the ServerFoo DeploymentManager to just pick up and start using.
See LikeJettyXml.java for an example how this works.
DeploymentManager deployer = new DeploymentManager();
DebugListener debug = new DebugListener(System.err,true,true,true);
server.addBean(debug);
deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
deployer.setContexts(contexts);
deployer.setContextAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
WebAppProvider webapp_provider = new WebAppProvider();
webapp_provider.setMonitoredDirName(jetty_base + "/webapps");
webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml");
webapp_provider.setScanInterval(1);
webapp_provider.setExtractWars(true);
webapp_provider.setConfigurationManager(new PropertiesConfigurationManager());
deployer.addAppProvider(webapp_provider);
server.addBean(deployer);
Related
I have code for starting a web app in Jetty. I want to add reverse proxy handling inside the app. When I call API,
I get error UnavailableException: Init parameter 'proxyTo' is required.
Server server = new Server(port);
WebAppContext webapp = createWebAppContext();
ServletHolder proxyServlet = new ServletHolder(ProxyServlet.Transparent.class);
proxyServlet.setInitParameter("ProxyTo", "http://attachments.dev.balcia.com:30310/api/");
proxyServlet.setInitParameter("Prefix", "/api");
webapp.addServlet(proxyServlet, "/api/attachments/*");
server.setHandler(webapp);
The init-parameter is called proxyTo not ProxyTo.
Servlet init-parameters are case sensitive.
I am using the Restlet Framework, but now I want to change to a proper server instead of using localhost.
I have already added my php files (they access the java files using the rest_server URL) to the server's folder and my java files as well, but I am not sure how to change the code so it identifies where the new location of the files is.
Here is the code from IdentiscopeServer (constructor empty):
public static void main(String[] args) throws Exception {
//setsup our security manager
if (System.getSecurityManager() == null){
System.setSecurityManager(new SecurityManager());
}
identiscopeServerApp = new IdentiscopeServerApplication();
IdentiscopeServer server = new IdentiscopeServer();
server.getServers().add(Protocol.HTTP,8888);
server.getDefaultHost().attach("", identiscopeServerApp);
server.start();
}
I guess that the correct line to change is the one with "Protocol.HTTP, 8888". If the address of my new server is http://devweb2013.co.uk/research/Identiscope, how exactly do I set this up? Is there anything else necessary for it to work apart from just moving the files to a folder in the server?
The IdensticopeServerApplication is the following:
public class IdentiscopeServerApplication extends Application {
public IdentiscopeServerApplication() {
}
public Restlet createInboundRoot() {
Router router = new Router(getContext());
//attaches the /tweet path to the TweetRest class
router.attach("/collectionPublic", CollectionPublicREST.class);
router.attach("/collectionPrivate", CollectionPrivateREST.class);
router.attach("/analysis", AnalysisREST.class);
return router;
}
}
Thank you in advance, it is my first time using this Framework.
If I understand you correctly, you just want to run your main() method as the server, correct? In this case, the code for main() needs to be in a location that -- when running -- can provide the service at http://devweb2013.co.uk/research/Identiscope. Since you haven't stated what kind of server you are putting the code, I can't say where the best place to put the code would be. I assume you have superuser privileges on your deployment server, since the URL you provided implies port 80 will be serving your Identiscope web service (port 80 is a privileged port on most OS's). So as an answer, I can only provide general information.
On your deployment server, port 80 must be free (i.e. nothing else should be acting as a web server on port 80 on that machine) and the IdentiscopeApplication must be running on port 80. To do that, you need only change the line:
server.getServers().add(Protocol.HTTP,8888);
to:
server.getServers().add(Protocol.HTTP, 80);
then run the application as a user that is allowed to start servers on port 80 (preferably NOT the superuser). If you haven't already, you will need to get Java running on your deployment server and make sure all Restlet libraries are in the classpath where you plan to run your application.
If I understand what you are trying to do, then this should do the trick.
What is the method call to figure out what is the url and port a jetty servlet is running on? So that I can just print it on the screen, and use it in the client to connect:
url = new URL("trying to figure this out");
I am running both the client and the server locally, in eclipse, this is a school project.
Relevant code:
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class ServerMain {
public static void main(String[] args){
Server server = new Server();
ServletContextHandler context =
new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("Servlet");
context.setResourceBase("etc/docroot");
server.setHandler(context);
context.addServlet(new ServletHolder(new MyServer()), "/game");
DefaultServlet staticFileServlet = new DefaultServlet();
context.addServlet(new ServletHolder(staticFileServlet), "/*");
System.out.println("context: " + context.getContextPath());
//System.out.println("One valid Port = "
+ context.getServer().getConnectors()[0].getPort());
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Since you are doing this embedded you have a couple of options.
Server server = new Server(8080);
This will start the server on port 8080, which is fine if that is what you want but in unit tests you generally want this to be a random port to avoid issues with things like CI's or parallel tests tripping on each other.
Server server = new Server(0);
This will start the connector on a random port, but how to get that port?
server.getConnectors()[0].getLocalPort();
The port is really coming from the connector, and typically you only are setting up one connector here, so this gets you that port.
Now localhost works for testing well, but if you wanted the name of the host that the connector is on you can use this:
server.getConnectors()[0].getHost();
Chain that stuff up and you get how we do most of our unit testing in jetty itself, spinning up the server itself, wiring up whatever handler or webapps we want and then assert behaviors, requests, responses, etc.
We have a number of embedded examples here that you can look at for different ways to wire up jetty inside code, and the jetty.xml format is just a thin layer of xml over java so you can map the startup of jetty easily through the code by reading the xml file with a java hat on. There is an embedded example in here for bootstrapping jetty based on that xml format as well if you want to keep the config there.
http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded
For a good page on embedding jetty, look here: http://www.eclipse.org/jetty/documentation/current/embedding-jetty.html
Most likely the jetty server is running on 8080 as default. If not you can go and check the server.xml file which should tell you what the port configuration is.
I'm not sure what you're asking here but if you had some servlet i.e Hello :
public final class Hello extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("URL is " + request.getRequestURL() + " port is " request.getServerPort());
}
}
Additionally if this is not info what you're looking for please see this :
http://docs.oracle.com/javaee/1.4/api/javax/servlet/http/HttpServletRequest.html
http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/api/javax/servlet/http/HttpServletRequest.html
I'm using Jetty 6 as an embedded web server in my Java app. Heretofore, I've had no reason to serve static content, but now I'd like to not only serve static content but also show directory listings.
I've tried using the ResourceHandler class to do this, but at some point mortbay removed the ability for ResourceHandler to do directory listing.
I'd like to do this without adding .jsp or servlet functionality and without web.xml configuration. In short I'm trying to do this programmatically.
For the life of me, I can't find any examples for this online. Could someone point me in the right direction?
Thanks much!
Okay, I figured out how to get Jetty to do what I wanted, which once again was to host some static content in addition to handling some custom servlets.
Ostensibly, the way to do this was to create a DefaultServlet and set the resourceBase and pathSpec accordingly, to allow me to host some directory on /www/*. However, this never worked. In fact, I couldn't find any explanation as to how the pathSpecs actually work or are supposed to be defined.
Thus, I had to create an additional ServletHandler and Context and add both my orginal Context and the new one for static content hosting to the Server.
I did that like so:
Server srv = new Server( port );
// create context and handler for my servlets
Context ctx = new Context();
ServletHandler sh = new ServletHandler();
// ... adding servlets here ...
// create context and handler for static content
ServletHandler sh2 = new ServletHandler();
ServletHolder holder = new ServletHolder( new DefaultServlet() );
holder.setInitParameter("resourceBase", staticResourceBase);
sh2.addServletWithMapping( holder, "/*" );
staticContext.setContextPath(staticPathSpec);
staticContext.setServletHandler(sh2);
// add both contexts to server
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[] { staticContext, ctx });
srv.setHandler(contexts);
This may not be the preferred way to do this, but it did allow me to add static content hosting to my Jetty-based app programmatically.
If you have a webapp and just jetty running, I think the default is to serve any static content out of the webapp root directory (e.g. the same directory that WEB-INF resides in). So for example you might have the following directories:
mywebapp
- WEB-INF
- static
- site_img.gif
And you can now serve http://mysite.com/static/site_img.gif
Correct me if I'm wrong and I'll remove this answer, this is just off the top of my head.
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
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.
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.
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);
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.
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!