I've got the following embedded Jetty setup:
ServletContextHandler topHandler = new ServletContextHandler(server, contextPath);
// Set path of static resources
topHandler.setBaseResource(...);
// Bind dynamic content to /api
RootResource rootResource = new RootResource();
FilterHolder restApiHandler = new FilterHolder(rootResource);
for (Entry<String, String> parameter : initParams.entrySet())
restApiHandler.setInitParameter(parameter.getKey(), parameter.getValue());
topHandler.addFilter(restApiHandler, "/api/*", EnumSet.allOf(DispatcherType.class));
// Bind static content to /
ServletHolder staticResourceHandler = topHandler.addServlet(DefaultServlet.class, "/");
server.start();
And I found documentation on Hot Deployment at https://www.eclipse.org/jetty/documentation/9.4.19.v20190610/hot-deployment.html but I don't understand how to put these together.
How do I get Jetty to reload servlets after their class files are reloaded so I don't have to restart the server every time I modify a Java file during development?
Hot Deployment is typically a feature of a WebAppContext and the WAR concept that provides isolated ClassLoaders.
The ServletContextHandler would need a custom ClassLoader to mimic the isolated classloader behaviors that the WebAppContext provides.
Hot Deployment is a feature of the DeploymentManager and an associated AppProvider that does the scanning to detect changes (like on a file system).
You'll want a DeploymentManager as a bean on your Server.
And you'll want to select a AppProvider (such as WebAppProvider) to monitor a directory for changes and trigger new App updates back to the DeploymentManager.
Next, you'll want to have your ServletContextHandler declared entirely in the XML deployable format in that monitored directory.
The classes you are modifying need to come from somewhere that ISN'T part of the Server ClassLoader.
The XML deployable you are using will need to create this isolated custom classloader and load the classes from this new (non-server) location.
These combined are what you are looking at for Hot Deployment.
Related
I have a pretty simple servlet setup with
Jersey
no web.xml
Tomcat 9
Maven for creating the .war and handling dependencies
Now I need to deploy a test and a production version of the servlet on the server and I a trying to use the individual context.xml file for each environment. A quote from the docs
Individual Context elements may be explicitly defined:
In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The context path and version will be derived from the base name of the file (the file name less the .xml extension). This file will always take precedence over any context.xml file packaged in the web application's META-INF directory.
All this also sounds easy here:
To give an example: if we wanted to deploy three installations of an application for test, stage and production, we would create three context.xml files:
tomcat/conf/catalina/localhost/test.xml
tomcat/conf/catalina/localhost/stage.xmltomcat/conf/catalina/localhost/prod.xml
And then deploy the same .war file three times as:
tomcat/webapps/test.war
tomcat/webapps/stage.war
tomcat/webapps/prod.war
And each installation would pick up its specific configuration automatically.
You can also read this documentation:
For Tomcat 5.0.x and later, WTP 2.0.x and later offers the opportunity to write the contexts to separate XML files in the proper folder (typically conf/Catalina/localhost) according to the requirements of that particular version. This behavior is enabled by checking the Publish module contexts to separate XML files option in the Server Options section of the Tomcat server editor. Note that only contexts for added projects will be written to separate XML files. Manually added contexts in server.xml will remain there.
There are several instructions, how to retrieve a value from the context.xml. For example:
<Environment name="companyName" value="My Company, Incorporated" type="java.lang.String" />
Can be used by
InitialContext context = new InitialContext();
Context xmlNode = (Context) context.lookup("java:comp/env");
String companyName = (String) xmlNode.lookup("companyName");
But this was listed for a Spring setup, how can this be done in a Jersey ResourceConfig based application/servlet?
For example:
#ApplicationPath("/")
public class MyMain extends ResourceConfig {
private final Logger logger = LoggerFactory.getLogger(MyMain.class);
public MyMain() {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
String myEnv = (String) envCtx.lookup("my-env");
this.logger.debug("Env: {}", myEnv);
} ...
is running into NamedExceptions: javax.naming.NameNotFoundException: Name [my-env] is not bound in this Context. Unable to find [my-env].
Is there any way to get the context configuration with Jersey, or do I need to use a different approach?
I also have no clue, how to debug the InitialContext. So the file is there and it is read by Tomcat, but I don't know how I can access it in the application. Do I need to use ServletContext.getInitParameter() instead - and how?
Update
My Eclipse setup seems to be the problem, because the published xml file is not the original, individual context.xml in my /Catalina/localhost folder. Is there any way to make sure that the original file is published in the Eclipse-Tomcat server?
"Publish module contexts to separate XML files" is checked. What is Update context paths? in publishing options (no effect visible, though)?
Here is the :
Given that:
I'd like to deploy webapp, packaged as WAR having web.xml in it, to Jetty server.
Within that app, I'd like to be able to have a JSR-356 specified javax websocket endpoints configured. I prefer, that those endpoints to be provided via ServerEndpointConfig, not an annotation scan.
There are many resources exemplifying that with an embedded Jetty, utilizing already famous WebSocketServerContainerInitializer.configureContext(context); API. I can't do that, obviously.
There are others, jumping directly to ServletContextListener and obtaining the ServerContainer via famous context.getAttribute("javax.websocket.server.ServerContainer"). So far I'm getting pretty much NULL via this API, so obviously container is not added.
Question:
What is that bit of configuration that is missing? Can it be done, preferably, via web.xml? If it is about config files like jetty.xml or jetty.ini - example would be nice, again, preferably for xml syntax.
Update:
As per answer below (the accepted one) and as I've actually tried to describe here - the known way of configuration is absolutely working just fine. Saying known I mean either by adding --module=websocket to some *.ini file for a non-embedded Jetty, or by calling WebSocketServerContainerInitializer.configureContext for an embedded one.
So rephrasing the question: is there any experience/knowledge from someone to enable websocket module by purely XML based configuration?
If using the ${jetty.base} and ${jetty.home} recommended installation process for Standalone Jetty, you should go to your ${jetty.base} instance directory and enable the websocket module.
$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --add-to-start=websocket
$ grep "websocket" start.ini
--module=websocket
Now you have websocket enabled for that ${jetty.base} instance.
If you want Jetty to discover your Server WebSocket endpoints via bytecode scanning your deployed webapps for annotations, then you'll also want the annotations module.
$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --add-to-start=annotations
$ grep "annotations" start.ini
--module=annotations
Once that's complete, you can do one (or more) of the following to have the websocket server endpoints deployed with your webapp.
Simply annotate your class with #ServerEndpoint (from javax.websocket.server.ServerEndpoint)
Or if you want to programmatically add WebSocket Server Endpoints, you have 2 options.
Provide an implementation of javax.websocket.server.ServerApplicationConfig in your project and return the Server endpoints you want Jetty to deploy.
During WebApp startup/initialization obtain the javax.websocket.server.ServerContainer from the ServletContext.getAttribute("javax.websocket.server.ServerContainer") and use it's addEndpoint() methods. Note that this is only possible from either a ServletContextListener.contextInitialized(ServletContextEvent sce) or ServletContainerInitializer.onStartup(Set<Class<?>> c, ServletContext ctx)
Why does this work in standalone Jetty? What is standalone Jetty doing to make this possible?
The following happens:
The websocket module adds the lib/websocket/*.jar to the server classpath
The websocket module depends on both client and annotations modules
The client module adds lib/jetty-client-<jetty.version>.jar to the server classpath
The annotations module adds lib/jetty-annotations-<jetty.version>.jar and lib/annotations/*.jar to the server classpath
The annotations module depends on the plus module
The annotations module selects etc/jetty-annotations.xml for execution on startup
The annotations module adds JPMS modules by name org.objectweb.asm
The plus module adds lib/jetty-plus-<jetty.version>.jar to the server classpath
The plus module selects etc/jetty-plus.xml for execution on startup
The plus module depends on the server, security, jndi, webapp, and transactions modules
(I'll skip the rest of the modules that are selected this way)
In short, with just adding websocket module you gain the following server classpath entries
lib/websocket/*.jar
lib/jetty-client-<jetty.version>.jar
lib/jetty-annotations-<jetty.version>.jar
lib/annotations/*.jar
lib/jetty-plus-<jetty.version>.jar
And the following XML files
lib/jetty-annotations.xml
lib/jetty-plus.xml
Both of these XML files simply modify the default Configuration list on the server side, making the Configuration behavior they introduce available to all deployed WebApps.
You can alternatively set the Configuration on the WebAppContext (before it's started) for webapp specific behaviors.
Example:
WebAppContext context = new WebAppContext();
context.setContextPath("/");
context.setBaseResource(Resource.newResource(rootResourceUrl));
context.setConfigurations(new Configuration[] {
new AnnotationConfiguration(),
new WebXmlConfiguration(),
new WebInfConfiguration(),
new PlusConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration()});
handlerList.addHandler(context);
Note: for javax.websocket you must use a WebAppContext, as the behaviors defined for its initialization require a full Web App to function.
While you can use a ServletContextHandler with javax.websocket endpoints, this style is 100% manually defined, intialized, and declared, with no automatic bytecode / annotation scanning features that JSR-356 relies on.
You can see all of this from the command line too.
Show the active ${jetty.base} configuration, what the XML property values are, what the server classpath is, and what XML is going to be executed (and in what order!!)
$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --list-config
Show the list of modules and how they relate (along with which ones are selected in your ${jetty.base} configuration)
$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --list-modules
We have a nested multi-module project. Our developers are a mix of IntelliJ IDEA and Eclipse users.
When running a jetty server inside an inner module, it seems we need to set the resource base to different values depending on which IDE we are using.
For IntelliJ:
root.setResourceBase("myModule/src/main/webapp");
For Eclipse:
root.setResourceBase("src/main/webapp");
We don't want to have to tweak our IDEs to make it work, e.g. I don't want to have to change some setting in IntelliJ to make it work with the Eclipse version of the code.
Any ideas?
The short answer:
Your execution differences between Eclipse vs Intellij can be explained by having different PWD, or ${user.dir}, or working directory setups.
The better answer:
Don't use filesystem paths then.
Look up a known resource in that location via a Classloader.getResource() and then pass the parent directory into the root.setResourceBase()
Example:
Server server = new Server(8080);
// Figure out what path to serve content from
ClassLoader cl = WebAppContextFromClasspath.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("hello.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("WebRoot is " + webRootUri);
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setWar(webRootUri.toASCIIString());
webapp.setParentLoaderPriority(true);
server.setHandler(webapp);
server.start();
server.join();
You can see this in the embedded-jetty-cookbook examples:
WebAppContextFromClasspath.java
ResourceHandlerFromClasspath.java
The other better answer:
Another approach is to find the src/main/webapp a few different ways depending on how it is being run
See the operational modes in the ServerMain.java in the embedded-jetty-live-war example.
I'm trying to find out if we can load a oracle commerce component from file system. Generally we assemble all the code into an ear file and deploy it, however, I got a requirement where in I have to store some components in file system rather than packaging them along with ear file.
I know that we can use URLClassloader to load a class as shown below,
File classDir = new File("A:\\LodeeModule\\classes");
URL[] url = { classDir.toURI().toURL() };
ClassLoader loader = new URLClassLoader(url);
for (File file : classDir.listFiles()) {
String filename = file.getName().replace(".class", "");
loader.loadClass("com.buddha.testers." + filename).getConstructor().newInstance();
}
but how can we use the same for an component which has to be resolved by Nucleus at later point of time? Is there any way to instruct Nucleus to resolve component from file system?
You should just be able to add the JAR that contains the components classes to the CLASSPATH system variable used by the application server instance.
Then in the component configuration just define the implementing class as you normally would
$class=some.class.path.class
If you are using Jboss EAP 6+ on a newer version of ATG (11.0+) you might have some more trouble, you have to jump through some more hoops due to its classloader
https://docs.jboss.org/author/display/AS7/Class+Loading+in+AS7
Essentially you would need to define a jboss module containing your jar files, and define a dependency between the ear's "module" and the module containing your classes.
Alternatively you can define a ClassLoaderService that will manage the classes for your JARs
To do this, you need to define a new ClassLoaderService, so create a new properties file as you would with any other component.
/my/custom/ClassLoaderService.properties
$class=atg.nucleus.ServicesManifestClassLoaderService
$description=Custom Class Loader Service.
# The files to go into the classpath of the classloader
classpathFiles=\
/path/to/my/jars/lib/someClasses.jar,\
/path/to/my/jars/lib/someOtherClasses.jar
loggingDebug=false
Then in the actual component that you need these classes for add this line;
$classloader=/my/custom/ClassLoaderService
I think you're looking for the atg.dynamo.data-dir property. If you specify that property dynamo will look at that location for the "server configs" or properties files. This allows you to separate the configs from the ear file.
Note: You can still include configs in the ear, I believe they will still have first precedence
It's usually specified when you start the server, something like:
run.sh -c <your server> -Datg.dynamo.data-dir=/data/something/serverconfigs
This feature is largely undocumented, but many people know about it.
See http://docs.oracle.com/cd/E24152_01/Platform.10-1/ATGPlatformProgGuide/html/s0302developmentmodeandstandalonemode01.html
EDIT:
I mistook what you were originally asking. You might want to take a look at the disposable class loader that ATG provides, but keep in mind this is only intended for development purposes.
I created a Netbeans Platform Application using Netbeans 7.0.1 and the JDK 1.7.
I implemented my own Web Application on a normal module using Embedded Jetty 7.4.5 (consisting of a Web Service and a couple of servlets), and I created a Library Wrapper Module including all the Jetty jar files and the "jetty-j2sehttpspi-7.4.5.v20110725.jar" that I needed to be able to publish the Web Service's Endpoint. The Web module has a dependency on the Jetty module.
The code I'm using is this:
System.setProperty("com.sun.net.httpserver.HttpServerProvider",
"org.mortbay.jetty.j2sehttpspi.JettyHttpServerProvider");
server = new Server();
JettyHttpServerProvider.setServer(server);
//We read the config file
String[] configFiles = {"etc/jetty.xml"};
for(String configFile : configFiles) {
XmlConfiguration configuration =
new XmlConfiguration(new File(configFile).toURI().toURL());
configuration.configure(server);
}
// Web Services
QueryWeb qWS = new QueryWeb();
Endpoint.publish("http://0.0.0.0:" +
(server.getConnectors()[0].getPort()) + "/ws", qWS);
// Servlets
HandlerCollection hc = (HandlerCollection)server.getHandler();
ServletContextHandler sch =
new ServletContextHandler(ServletContextHandler.SESSIONS);
sch.setContextPath("/web");
sch.addServlet(stream.class, "/stream");
// We add the servlet handler to the server's context handler collection
// because it's the one used by the Web Service Endpoint
ContextHandlerCollection chc = (ContextHandlerCollection)hc.getHandlers()[0];
chc.addHandler(sch);
server.start();
When I try and run the application, I get the following error after the "Endpoint.publish" call:
Exiting C:\Program Files (x86)\NetBeans 7.0\harness\run.xml.
Exiting C:\Program Files (x86)\NetBeans 7.0\harness\run.xml.
C:\Program Files (x86)\NetBeans 7.0\harness\suite.xml:500:
The following error occurred while executing this line:
C:\Program Files (x86)\NetBeans 7.0\harness\run.xml:225:
The following error occurred while executing this line:
C:\Program Files (x86)\NetBeans 7.0\harness\run.xml:193:
The application is already running within the test user directory.
You must shut it down before trying to run it again.
As far as I understand, this is happening because the system can't find the "org.mortbay.jetty.j2sehttpspi.JettyHttpServerProvider" class. Therefore it defaults back to the web server included in the JDK, which causes a conflict since we get both web Servers (Jetty and the JDK's) trying to run on the same port (in this case it's 8081).
The only way I managed to fix this problem was by copying all the Jetty jar files into the JRE's "lib/ext" folder (copying only the "jetty-j2sehttpspi-7.4.5.v20110725.jar" results in no errors, but the server won't start). In this way the system can find the class it needs and all it's dependencies.
I suppose that what's going on is that even if NetBeans uses it's own classpath loader, the System.setProperty method is ignoring this and trying to access the standard classpath, and since a NetBeans Platform Application doesn't actually let you change the classpath directly (that would beat the whole purpose of having modules administered by the NetBeans platform), I don't really know how to make it use the library included in the wrapper module.
I can keep developing the application with the temporary solution I found, but honestly, copying stuff into the JRE folders is not an acceptable solution and will eventually result in distribution and installation problems in client machines (already tried it in a Mac OS machine and I didn't even know where the JRE kept it's libraries to try and do the same dirty trick).
Therefore I want to ask you guys if there is any solution to this particular problem or if anyone has a better explanation of what's going on and how I might fix it without having to recreate the whole architecture of my project (which actually works OK except for this little inconvenient).
Thanks in advance!
Write your question to the mailing list, dev#platform.netbeans.org, and you're more likely to get an answer.