Can I set jetty classloading semantics using system properties? - java

In our project, we start embedded Jetty using only the Java API (no external xml, etc.), and then deploy war bundles to it. Now I have a situation where I'm getting ClassCastExceptions because of differing jars loaded from the .war vs. what is on the actual classpath. Reading the Jetty page on classloading (https://www.eclipse.org/jetty/documentation/current/jetty-classloading.html) I wanted to see if I could configure the WebAppClassLoader to augment the set of classes considered to be 'system' classes. There is a Java API to do this (WebAppContext.setServerClasses()) and a way to do it if you're using an xml config file:
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="serverClasses">foo.bar.,com.acme.</Set>
...
But I'd like to know if it is possible to do it using just Java system properties.
Thanks!

There are no system properties to configure server or system classes on a WebAppContext.
This is because that kind of change is considered to belong to a specific WebApp, not all WebApps.
But, you have an alternative, if you are using the DeploymentManager in your embedded-jetty, you are in luck, you have an option in code.
You'll want to create a custom AppLifeCycle.Binding which sets those properties on any deployed WebAppContext (I would recommend binding to deploying).
Here's an example that forces the WebAppContext's to always use the logging libraries from the server/system classloader.
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.webapp.WebAppContext;
public class CentralizedWebAppLoggingBinding implements AppLifeCycle.Binding
{
public String[] getBindingTargets()
{
return new String[]
{ "deploying" };
}
public void processBinding(Node node, App app) throws Exception
{
ContextHandler handler = app.getContextHandler();
if (handler == null)
{
throw new NullPointerException("No Handler created for App: " + app);
}
if (handler instanceof WebAppContext)
{
WebAppContext webapp = (WebAppContext)handler;
webapp.addSystemClass("org.apache.log4j.");
webapp.addSystemClass("org.slf4j.");
webapp.addSystemClass("org.apache.commons.logging.");
}
}
}
And here's an example of using DeploymentManager from embedded-jetty (with the above CentralizedWebAppLoggingBinding too.
ContextHandlerCollection contexts = new ContextHandlerCollection();
DeploymentManager deployer = new DeploymentManager();
if(debugIsEnabled)
{
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 webAppProvider = new WebAppProvider();
webAppProvider.setMonitoredDirName(jettyBase + "/webapps");
webAppProvider.setDefaultsDescriptor(jettyHome + "/etc/webdefault.xml");
webAppProvider.setScanInterval(1);
webAppProvider.setExtractWars(true);
webAppProvider.setConfigurationManager(new PropertiesConfigurationManager());
webAppProvider.addLifeCycleListener(new CentralizedWebAppLoggingBinding());

Related

How is configuration read in AKKA

I am creating custom configurations for my akka application written in java as follows
application.conf
akka {
actor {
debug {
# enable DEBUG logging of actor lifecycle changes
lifecycle = on
}
}
}
I am not sure how to load this configuration. It is unclear from the docs if this needs to be explicitly loaded at actrSystem creation or if it needs to be in the class path while running the jar. In both cases, is there an example I can look at
This is a maven project and the configuration file is under src/main/resources
I see that the applucation.conf exists under target/classes. Is that it?
With ConfigFactory you can load the application.conf file from src/main/resources, not necessarily need an ActorSystem to use it
According to the akka doc http://doc.akka.io/docs/akka/2.4/general/configuration.html
Akka uses the Typesafe Config Library, which might also be a good choice for the configuration of your own application or library built with or without Akka. This library is implemented in Java with no external dependencies; you should have a look at its documentation (in particular about ConfigFactory), which is only summarized in the following.
A simple test
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
public class ConfigFactoryTest {
public static void main(String[] args) {
Config conf = ConfigFactory.load("application.conf");
System.out.println(conf.getString("akka.actor.debug.lifecycle")); //should be 'on'
}
}
Also can use withFallback method
http://doc.akka.io/docs/akka/2.4/general/configuration.html#Reading_configuration_from_a_custom_location
Assume you have another properties file called fallback.conf
akka {
actor {
fallback = "fallback"
}
}
Config fallback = ConfigFactory.load("fallback.conf")
Config conf = ConfigFactory.load("application.conf").withFallback(fallback);
System.out.println(conf.getString("akka.actor.fallback")); //fallback
Here you can find examples :
https://github.com/typesafehub/config/tree/master/examples/java

Is using VM arguments for servlet configuration 'clean'?

This relates to this answer:
System.getProperty("catalina.base") There can be scenario where client may use any other server
another server-independent system
property yourself, you can set as a VM
argument.
-Dconfig.location=/path/to/folder
In case of Tomcat, you can set it as
JAVA_OPTS environment variable, or
edit the catalina.bat startup file or
edit the Windows Service settings
(when it's installed as Windows
Service), etc. Other servers supports
similar constructs as well.
Is this considered 'clean'? We've been doing this for years, just want to know if this is acceptable, or there is a better way to configure runtime environment.
It feels maybe dirty, but there are apart from putting it in the classpath really no better ways if the requirement is to untouch the WAR whenever you want to change the location of external configuration files.
If untouching the WAR is not a strict requirement and rebuilding the WAR is allowed (e.g. you're using an inhouse developed application with continuous integration and serveradmins are in the same line, etc), then you could also use a <context-param> in web.xml instead.
<context-param>
<param-name>config.location<param-name>
<param-value>/path/to/file</param-value>
</context-param>
It's then in any Servlet (or better, ServletContextListener) available by ServletContext#getInitParameter():
String configLocation = servletContext.getInitParameter("config.location");
File configFile = new File(configLocation, "config.properties");
// ...
My understanding is that "more clean" would be using either <servlet-param> <init-param> in web.xml or some kind of IoC solution, like Spring.
I feel this is not the cleanest of ways to attain what you want. You can use the web.xml init params or servlet params tags.
Another way is using properties file or an XML configuration file.
I just solved a similar problem in a slightly different way. Our customer wants to configure database connection details, integration server locations and ports etc. without rebuilding the war. Using environment property to point an external file containing the information may or may not be okay, but it felt a bit dirty trick. Anyway, here's a slightly more enterprisey way.
For database connections we use JNDI lookup and below is the current solution for integration server parametrization. The parameters can come from at least three different sources now:
properties-file, which is overridable with Maven profiles and requires single line of xml in spring configuration to be accessible. This is obviously inside the war file.
web.xml context-param. This is also, of course, inside the war file.
Tomcat server can override the init parameters with context.xml which can be outside the war. This happens to be the same file where JNDI context is defined, which is nice.
Below is the implementation for configuration accessor bean. It can run in servlet context and also without one (for some unit tests it makes little sense to kickstart a full-blown web server, but we nevertheless need to satisfy spring bean injections).
I don't mean this to be a perfect solution, but it is one. Didn't find anything like this with google.
#Service
public class IntegrationConfigurationImpl implements
IntegrationConfiguration, InitializingBean,
ServletContextAware, ApplicationContextAware {
private static final String SERVER_HOST_PROPERTY = "integration.server.host";
private static final String SERVER_PORT_PROPERTY = "integration.server.port";
private static final String PROPERTY_BEAN_NAME = "integrationProperties";
private ServletContext servletContext;
private ApplicationContext applicationContext;
private static final Logger log = LoggerFactory.getLogger(IntegrationConfigurationImpl.class);
private String serverHost = "foo";
private int serverPort = 42;
#Override
public String getServerHost() {
return serverHost;
}
#Override
public int getServerPort() {
return serverPort;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void afterPropertiesSet() throws Exception {
// konfiguraation validointi..
if (servletContext == null) {
log.info("servlet context not set, not running as a web application. Trying to get properties from application context");
if (applicationContext.containsBean(PROPERTY_BEAN_NAME)) {
Properties p = (Properties)applicationContext.getBean(PROPERTY_BEAN_NAME);
serverHost = p.getProperty(SERVER_HOST_PROPERTY);
serverPort = Integer.valueOf(p.getProperty(SERVER_PORT_PROPERTY)).intValue();
} else {
log.info("Property bean not found :" + PROPERTY_BEAN_NAME);
}
} else {
serverHost = servletContext.getInitParameter(SERVER_HOST_PROPERTY);
serverPort = Integer.valueOf(servletContext.getInitParameter(SERVER_PORT_PROPERTY)).intValue();
}
log.info("Using integration server " + getServerHost() + ", port " + getServerPort());
}
}
The disadvantage with having system property is you need to restart the container to modify the system parameter.
Having it as init-param in web.xml, can allow you to modify by just restarting the web app.
Having in init-param is a better way.

how to share a jsf error page between multiple wars

I'm trying to share an error page (error.xhtml) between multiple wars. They are all in a big ear application, and all use a common jar library, where I'd like to put this.
The error page should use web.xml, or better web-fragment.xml, and would be declared as a standard java ee error page.
Actual EAR structure:
EAR
EJB1
EJB2
WAR1 (using CommonWeb.jar)
WAR2 (using CommonWeb.jar)
WAR3 (using CommonWeb.jar)
Just putting the error page under META-INF/resources won't work, as it's not a resource.
I'd like to have as little as possible to configure in each war file.
I'm using Glassfish 3.1, but would like to use Java EE 6 standards as much as possible.
You need to create a custom ResourceResolver which resolves resources from classpath, put it in the common JAR file and then declare it in web-fragment.xml of the JAR (or in web.xml of the WARs).
Kickoff example:
package com.example;
import java.net.URL;
import javax.faces.view.facelets.ResourceResolver;
public class FaceletsResourceResolver extends ResourceResolver {
private ResourceResolver parent;
private String basePath;
public FaceletsResourceResolver(ResourceResolver parent) {
this.parent = parent;
this.basePath = "/META-INF/resources"; // TODO: Make configureable?
}
#Override
public URL resolveUrl(String path) {
URL url = parent.resolveUrl(path); // Resolves from WAR.
if (url == null) {
url = getClass().getResource(basePath + path); // Resolves from JAR.
}
return url;
}
}
with in web-fragment.xml or web.xml
<context-param>
<param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
<param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>

Import location in wsit-client.xml to file in another jar, using an URL string to locate a file on the classpath

Normally the wsit-client.xml has import statements like this:
<import location="foo.xml" namespace="http://foo.org/" />
I've found that their can be online one wsit-client.xml on the classpath/META-INF, but can I refer to an xml who's located into another jar in that wsit-client.xml? Something like :
<import location="classPathResource/WEB-INF/foo.xml" namespace="http://foo.org/" />
I would like to create a single wsit-client.xml who contains the imports for all my webservices but I want to separate the configuration for all the different webservices in to different projects.
I've fixed it by creating an URLStreamHandler in the wsit-client.xml I can now define location="myprotocol://foo.xml"
I've used spring's PathMatchingResourcePatternResolver to locate my xml file in another project/jar.
public class Handler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL u) throws IOException {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
final URL resourceUrl = resolver.getResource(u.getPath()).getURL();
if (resourceUrl == null) {
throw new IOException(String.format("File %s not found on the classpath", u.getFile()));
}
return resourceUrl.openConnection();
}
}
I'm not using the VM arguments to define the handler but I've implemented an URLStreamHandlerFActory like explained over here URL to load resources from the classpath in Java
More info about writing your own protocol handlers can be find on this site: http://java.sun.com/developer/onlineTraining/protocolhandlers/
I've still got 1 project who contains the single wsit-client.xml with references to all my web service configurations, but at least I've managed to separate the configuration for all the different services in different maven projects.

Is there a way to access web.xml properties from a Java Bean?

Is there any way in the Servlet API to access properties specified in web.xml (such as initialization parameters) from within a Bean or Factory class that is not associated at all with the web container?
For example, I'm writing a Factory class, and I'd like to include some logic within the Factory to check a hierarchy of files and configuration locations to see which if any are available to determine which implementation class to instantiate - for example,
a properties file in the classpath,
a web.xml parameter,
a system property, or
some default logic if nothing else is available.
I'd like to be able to do this without injecting any reference to ServletConfig or anything similiar to my Factory - the code should be able to run ok outside of a Servlet Container.
This might sound a little bit uncommon, but I'd like for this component I'm working on to be able to be packaged with one of our webapps, and also be versatile enough to be packaged with some of our command-line tools without requiring a new properties file just for my component - so I was hoping to piggyback on top of other configuration files such as web.xml.
If I recall correctly, .NET has something like Request.GetCurrentRequest() to get a reference to the currently executing Request - but since this is a Java app I'm looking for something simliar that could be used to gain access to ServletConfig.
One way you could do this is:
public class FactoryInitialisingServletContextListener implements ServletContextListener {
public void contextDestroyed(ServletContextEvent event) {
}
public void contextInitialized(ServletContextEvent event) {
Properties properties = new Properties();
ServletContext servletContext = event.getServletContext();
Enumeration<?> keys = servletContext.getInitParameterNames();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = servletContext.getInitParameter(key);
properties.setProperty(key, value);
}
Factory.setServletContextProperties(properties);
}
}
public class Factory {
static Properties _servletContextProperties = new Properties();
public static void setServletContextProperties(Properties servletContextProperties) {
_servletContextProperties = servletContextProperties;
}
}
And then have the following in your web.xml
<listener>
<listener-class>com.acme.FactoryInitialisingServletContextListener<listener-class>
</listener>
If your application is running in a web container, then the listener will be invoked by the container once the context has been created. In which case, the _servletContextProperties will be replaced with any context-params specified in the web.xml.
If your application is running outside a web container, then _servletContextProperties will be empty.
Have you considered using the Spring framework for this? That way, your beans don't get any extra cruft, and spring handles the configuration setup for you.
I think that you will have to add an associated bootstrap class which takes a reference to a ServletConfig (or ServletContext) and transcribes those values to the Factory class. At least this way you can package it separately.
#toolkit : Excellent, most humbled - This is something that I have been trying to do for a while

Categories

Resources