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
Related
I have a Tomcat 8 instance which is used to publish multiple web applications.
As today, I have a Spring architecture which produces 2 .war:
Front-end
Back-end
And the result is the following:
http://localhost:8080/my-front-end
http://localhost:8080/my-back-end
What I would like to achieve is to have a common root path for both these wars. Is this something I can do in Tomcat? In JBOSS or IIS I can use the concept of "Virtual Directory" to achieve that.
My intended result is the following:
http://localhost:8080/my-app/my-front-end (my-front-end.war)
http://localhost:8080/my-app/my-back-end (my-back-end.war)
The Tomcat documentation says to use # in the name.
Context Name Base File Name Example File Names
/foo foo foo.xml, foo.war, foo
/foo/bar foo#bar foo#bar.xml, foo#bar.war, foo#bar
So use these names for your war files:
my-app#my-front-end.war
my-app#my-back-end.war
Set the context paths: /my-app/my-front-end and /my-app/my-back-end respectively.
For example, if you are using Spring Boot, then put:
server.contextPath=/my-app/my-front-end
and
server.contextPath=/my-app/my-back-end
in application.properties file.
If you cannot modify the application's source code, here you can find out how to configure Tomcat to get the same result.
I am having project A and project B, A has jar dependency of project B. I have defined log4j.xml in project A but I am not able to see logs of sub-project(B.jar) in file appender as well as tomcat server console. Does project B will take log4j.xml form parent project A or not then which config does it use?
There is one log4j config for your entire JVM (unless you're working in a containerized environment using class loaders and.... that's not what's described).
Missing log messages implies that the configuration from log4j either (a) isn't what you think it is (i.e. a different log4j.xml is being used) or (b) doesn't have the right settings for the missing log lines.
Adding the following to the JVM at startup may help:
-Dlog4j.debug
It may also be possible to browse the log4j settings via MBeans in jconsole.
If you want all apps (WAR files) in a Tomcat instance to have the same logging configs, the simple solution is to arrange that all WAR files have a copy of the same config file.
If you want the apps to share a common logging framework (with a single configuration), then you should consider using Context Selectors, as described in the Log4j 2 documentation.
Using Context Selectors
There are a few patterns for achieving the desired state of logging separation using ContextSelectors:
Place the logging jars in the container's classpath and set the system property log4j2.contextSelector to org.apache.logging.log4j.core.selector.BasicContextSelector. This will create a single LoggerContext using a single configuration that will be shared across all applications.
Place the logging jars in the container's classpath and use the default ClassLoaderContextSelector. Follow the instructions to initialize Log4j 2 in a web application. Each application can be configured to share the same configuration used at the container or can be individually configured. If status logging is set to debug in the configuration there will be output from when logging is initialized in the container and then again in each web application.
Follow the instructions to initialize Log4j 2 in a web application and set the system property or servlet context parameter log4j2.contextSelector to org.apache.logging.log4j.core.selector.JndiContextSelector. This will cause the container to use JNDI to locate each web application's LoggerContext. Be sure to set the isLog4jContextSelectorNamed context parameter to true and also set the log4jContextName and log4jConfiguration context parameters.
The exact method for setting system properties depends on the container. For Tomcat, edit $CATALINA_HOME/conf/catalina.properties. Consult the documentation for other web containers.
I don't think there is a direct equivalent in Log4j 1.x.
We are trying to setup JackRabbit Oak on one of our Linux machine with IBM WAS application server and facing some issues starting the sever on WAS.
The Apache JackRabbit is a content repository which is a hierarchical content store with support for structured and unstructured content, full text search, versioning, transactions, observation, and more. We are using it for storing digital assets in structured form (specifically, as a tree) as per our requirement.
We have a REST application and another application DAM (Digital Asset Management) which handles creation of repository, providing connection with the repo whenever requested (Singleton), creating nodes, etc. on the repository. In our local development setup we have exported this DAM as a JAR, say dam.jar and have put this in REST application’s class path. We have JackRabbit Oak’s JAR (oak-run-1.4.11.jar), which we have put in class path of DAM application.
While doing a similar setup on Linux machine on WAS application server, we are using the same dam.jar which is created by exporting the project as a JAR in eclipse on Windows machine. We put this in REST application’s class path on WAS by configuring this in the server on which REST app is deployed.
While debugging we found that application is able to access classes from dam.jar, but when DAM’s class tries to call JackRabbit’s classes it fails throwing java.lang.NoClassDefFoundError. We tried putting JackRabbit’s JAR (oak-run-1.4.11.jar) in the class path on WAS by configuring this for REST app’s server, but then server fails to start.
Attached is the log file: /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/servers/RESTAPP/configuration/1489493294429.log
There is nothing much inside server logs from /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/logs/RESTAPP (RESTAPP is the server name for our REST application), below is the snippet:
[3/14/17 17:38:04:872 IST] 00000001 ModelMgr I WSVR0801I: Initializing all server configuration models
[3/14/17 17:38:08:564 IST] 00000001 WorkSpaceMana A WKSP0500I: Workspace configuration consistency check is disabled.
[3/14/17 17:38:08:834 IST] 00000001 AdminTool A ADMU3200I: Server launched. Waiting for initialization status.
[3/14/17 17:38:18:241 IST] 00000001 AdminTool A ADMU3011E: Server launched but failed initialization. Server logs, startServer.log, and other log files under /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/logs/RESTAPP should contain failure information.
We suspect dam.jar here, as this has been created from our eclipse workspace by exporting it as a JAR file and this might need some extra information to have this JackRabbit Oak libraries included in that.
We are putting classpath entries in Application servers > RESTAPP > Process definition > Java Virtual Machine of WAS.
Thanks
I would try creating a "Shared library" in Websphere and putting your JackRabbit dependency jars in there:
Environment -> Shared libraries -> New...
Set then name "Jackrabbit", and then in the Classpath box add the paths to your jars.
Then in your REST application (Applications -> Application Types -> WebSphere enterprise applications -> [your application name])
Click "Shared library references" under "References"
Select the application and click "Reference shared libraries"
Select the Jackrabbit shared library and click the right arrow to reference that shared library from the application.
Classloading in WebSphere is very complicated -- see chapter 22 of the WebSphere Application Server V8.5 Administration and Configuration Guide for the full description. Trying to add classpath entries at the JVM definition level definitely won't work.
To piggyback on Andrew's answer above, the real key is to make sure that both dam.jar and oak-run-1.4.11.jar are within the same class loader, and that they are NOT in a server-level class loader - putting custom code in the server JVM class path makes it visible to the server runtime and can actually override server classes (which is probably why your server init failed after doing that).
The very easiest answer, assuming this is a simple web application, is to put both jars in the WEB-INF directory of your WAR module. Both will be loaded by the web app class loader, and they'll be able to see each other. If there's some reason you don't want them in the application itself, then Andrew's shared library suggestion would have the same practical effect.
I've written some code to create and run webservice client using CXF. I used JaxWsClientFactoryBean (not sure it's the best solution) to create client from .wsdl file.
The goal here was to do this programmatically avoiding Spring etc. Just pure code with Java and CXF.
JaxWsClientFactoryBean cfb = new JaxWsClientFactoryBean();
cfb.setAddress(getServiceProperty(intClass, PROPERTY_KEY_URL_SUFFIX));
cfb.setServiceClass(intClass);
cfb.setOutInterceptors(getOutInterceptors(intClass));
cfb.setServiceName(SERVICE_NAME);
cfb.setWsdlURL("classpath:wsdl/" + intClass.getSimpleName() + ".wsdl");
cfb.setEndpointName(ENDPOINT_NAME);
Client client = cfb.create();
ClientProxy cp = new ClientProxy(client);
I intService = (I)
Proxy.newProxyInstance(intClass.getClassLoader(), new Class[] { intClass }, cp);
I'm really not sure if this is done correctly, but it works when I run this code locally and when I deploy it on Tomcat.
Unfortunatelly I need to run this code on Weblogic and this results in strange exception:
Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was
made to insert a node where it is not permitted.
at org.apache.cxf.wsdl11.WSDLManagerImpl.loadDefinition(WSDLManagerImpl.java:235)
at org.apache.cxf.wsdl11.WSDLManagerImpl.getDefinition(WSDLManagerImpl.java:186)
at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:92)
... 26 more
Caused by: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted.
at com.sun.org.apache.xerces.internal.dom.ParentNode.internalInsertBefore(ParentNode.java:356)
at com.sun.org.apache.xerces.internal.dom.ParentNode.insertBefore(ParentNode.java:284)
at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.insertBefore(CoreDocumentImpl.java:399)
at com.sun.org.apache.xerces.internal.dom.NodeImpl.appendChild(NodeImpl.java:235)
at org.apache.cxf.staxutils.StaxUtils.readDocElements(StaxUtils.java:1019)
at org.apache.cxf.staxutils.StaxUtils.readDocElements(StaxUtils.java:939)
at org.apache.cxf.staxutils.StaxUtils.read(StaxUtils.java:866)
at org.apache.cxf.wsdl11.WSDLManagerImpl.loadDefinition(WSDLManagerImpl.java:226)
... 28 more
This happens during application deployment. It looks like there is something wrong with .wsdl file, but wait... It was working on Tomcat!
I think that there could be some difference in com.sun.org.apache.xerces.* classes implementation within Weblogic with its JRockit VM and standard JVM, but I have no idea how to solve it.
I spent many hours trying differend ways of client creation. Most of them worked locally and in Tomcat, but none on WebLogic.
Any hints what to try next? I'm kinda tired of this topic :D
I agree with your suspicion that the problem is related to the used version of Xerces. The stacktrace shows that the Sun implementation of Xerces which is derivative of the Apache Xerces is used in your case.
Please check the Apache CFX Application Server Configuration Guide instructions related to WebLogic.
WebLogic ClassLoading
In WebLogic Server, any .jar file present in the system classpath is loaded by the WebLogic Server system classloader. All applications running within a server instance are loaded in application classloaders which are children of the system classloader. In this implementation of the system classloader, applications cannot use different versions of third-party jars which are already present in the system classloader. Every child classloader asks the parent (the system classloader) for a particular class and cannot load classes which are seen by the parent.
For example, if a class called com.foo.Baz exists in both $CLASSPATH as well as the application EAR, then the class from the $CLASSPATH is loaded and not the one from the EAR. Since weblogic.jar is in the $CLASSPATH, applications can not override any WebLogic Server classes.
In order to use an alternate version of Xerces you have to create a FilteringClassLoader.
Usage of FilteringClassLoader
The FilteringClassLoader provides a mechanism for you to configure deployment descriptors to explicitly specify that certain packages should always be loaded from the application, rather than being loaded by the system classloader. This allows you to use alternate versions of applications such as Xerces and Ant.
The FilteringClassLoader sits between the application classloader and the system. It is a child of the system classloader and the parent of the application classloader. The FilteringClassLoader intercepts the loadClass(String className) method and compares the className with a list of packages specified in weblogic-application.xml file.
In conclusion, check the steps included in the Apache CFX Application Server Configuration Guide and take care to explicitly specify that the org.apache.xerces.* package is loaded from the application, rather than being loaded from the system classloader.
For example the weblogic-application.xml file in the META-INF should look like:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90">
<application-param>
<param-name>webapp.encoding.default</param-name>
<param-value>UTF-8</param-value>
</application-param>
<prefer-application-packages>
<package-name>javax.jws.*</package-name>
<package-name>org.apache.xerces.*</package-name>
</prefer-application-packages>
</weblogic-application>
I hope this helps.
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.