I am trying to get the server URL (eg. http://www.mywebapp.com/myapp) from the ServletContext when the application starts up, I am doing this by invoking a bean method on startup (using #Startup) and getting the servlet context,
#Startup
#Name("startupActions")
#Scope(ScopeType.APPLICATION)
public class StartupActionsBean implements StartupActions,
Serializable {
#Logger private Log log;
/**
*
*/
private static final long serialVersionUID = 1L;
#Create
#Override
public void create(){
ServletContext sc = org.jboss.seam.contexts.ServletLifecycle.getServletContext();
String context = sc.getContextPath();
String serverInfo = sc.getServerInfo();
log.debug("__________________START__________________");
log.debug("Context Path: "+context);
log.debug("Server Info: "+serverInfo);
}
// Cleanup methods
#Remove
#BypassInterceptors
#Override
public void cleanUp(){}
}
This work ok, however the ServletContext path is blank, see console output below..
18:52:54,165 DEBUG [uk.co.app.actions.startup.StartupActionsBean] __________________START__________________
18:52:54,165 DEBUG [uk.co.app.actions.startup.StartupActionsBean] Context Path:
18:52:54,165 DEBUG [uk.co.app.actions.startup.StartupActionsBean] Server Info: JBoss Web/3.0.0-CR1
Does anyone know how to get the contextpath through this, or other means?
ps. using SEAM 2.2.2, Jboss AS6 Final, Richfaces 3.3.3
Don't use #Startup, your component startup code gets called before contexts are fully setup and some other factories could not be yet initialized.
Observe the org.jboss.seam.postInitialization event and use the same ServletLifecycle.getCurrentServletContext() to get hold of the data needed. A quick example:
#Name("contextPath")
public class ContextPathInit implements Serializable {
private String contextPath;
#Observer("org.jboss.seam.postInitialization")
public void init() {
contextPath = ServletLifecycle.getCurrentServletContext().getContextPath();
}
}
Have you tried getting it from ExternalContext using the getContextName method?
FacesContext.getCurrentInstance().getExternalContext().getContextName()
Related
I have an application with management.server enabled:
management.server.port=8081
When I start application, I have:
10 threads for 8080 HTTP nio connector
10 threads for 8081 HTTP nio connector
But I would like to reduce min-spare only for management (8081) and not for the web application (8080)
Looking at Spring code, it seems it's not possible, can someone confirm ?
EDIT: The approach below is not sufficient as the ManagementWebServerFactoryCustomizer is also a ConfigurableWebServerFactory and will thus be applied to the main server.
Adding logic to check againgst the management port is not helping as the management context has its very own wiring and won't pick up the bean.
Looks like it's not possible to hook into the management server configuration easily (would be easier if ServletManagementContextFactory were public).
You can look into ServletManagementChildContextConfiguration to see how the management server is wired.
You could hook into the management server configuration by providing a ManagementWebServerFactoryCustomizer like this (not sure if there's an easier way):
#Configuration
public class TomcatManagementCustomizerConfiguration {
#Bean
ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> servletManagementWebServerFactoryCustomizer(
#Value("${management.server.threads.min-spare:5}") int managementMinSpareThreads,
ListableBeanFactory beanFactory) {
return new TomcatManagementCustomizer(beanFactory, managementMinSpareThreads);
}
static class TomcatManagementCustomizer extends ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
private final int managementMinSpareThreads;
protected TomcatManagementCustomizer(ListableBeanFactory beanFactory, int managementMinSpareThreads) {
super(beanFactory, TomcatWebServerFactoryCustomizer.class);
this.managementMinSpareThreads = managementMinSpareThreads;
}
#Override
#SuppressWarnings("rawtypes")
protected void customize(ConfigurableServletWebServerFactory factory, ManagementServerProperties managementServerProperties, ServerProperties serverProperties) {
super.customize(factory, managementServerProperties, serverProperties);
((TomcatServletWebServerFactory) factory).addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol protocol = (AbstractProtocol) handler;
protocol.setMinSpareThreads(managementMinSpareThreads);
}
});
}
}
}
Can you not just put the following in either properties file or YAML file?
Or is there something I misunderstood?
server.tomcat.threads.min-spare=2
(This is for properties file)
Just to verify (You don't need this as you have been checking the updated value in the log)
Put the following in either properties file or YAML file
management.endpoints.web.exposure.include=health,info,metrics,env
(This is for properties file)
And visit /actuator/env/server.tomcat.threads.min-spare
You need actuator dependency for the link above to work.
You can use #ManagementConfigurationContext and add the configuration class to to your META-INF/spring.properties file.
It is also important to place the configuration class in a package which is not the main package or sub-package of your main application context. This is so that this configuration only applies to the management context.
Below is the sampel configuration following #Holgzn's response.
#ManagementContextConfiguration
public class TomcatManagementCustomizerConfiguration {
#Bean
ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> servletManagementWebServerFactoryCustomizer(
#Value("${management.server.threads.min-spare:5}") int managementMinSpareThreads,
ListableBeanFactory beanFactory) {
return new TomcatManagementCustomizer(beanFactory, managementMinSpareThreads);
}
static class TomcatManagementCustomizer extends ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
private final int managementMinSpareThreads;
protected TomcatManagementCustomizer(ListableBeanFactory beanFactory, int managementMinSpareThreads) {
super(beanFactory, TomcatWebServerFactoryCustomizer.class);
this.managementMinSpareThreads = managementMinSpareThreads;
}
#Override
#SuppressWarnings("rawtypes")
protected void customize(ConfigurableServletWebServerFactory factory, ManagementServerProperties managementServerProperties, ServerProperties serverProperties) {
super.customize(factory, managementServerProperties, serverProperties);
((TomcatServletWebServerFactory) factory).addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol protocol = (AbstractProtocol) handler;
protocol.setMinSpareThreads(managementMinSpareThreads);
}
});
}
}
}
The spring.properties file
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=<package>.TomcatManagementCustomizerConfiguration
My company is currently using an old javaee jar (5.2.0) which doesn't contain the AroundTimeOut class. The jar cannot be changed because it will occur some major impacts. My manager asks me to intercept a timeout method.. Do you know if there is a way to workaround this issue?
This is the method that I use:
#Timeout
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Interceptors(CorrelationIdInterceptor.class)
public void handleTimeout(Timer timer) {
And the interceptor:
public class CorrelationIdInterceptor {
private static final String CORRELATION_ID = "CORRELATION_ID";
private static final String GET_CORRELATION_ID = "getCorrelationId";
private static final Logger LOGGER =
Logger.getLogger(CorrelationIdInterceptor.class);
#AroundInvoke
public Object log(final InvocationContext ic) throws Exception {
String webServiceMethodName = ic.getMethod().getName();
if(webServiceMethodName.equalsIgnoreCase("handleTimeout")){
webServiceMethodName="SIBOI"+":"+ webServiceMethodName;
}
Try to add in your ejb-jar.xml
<assembly-descriptor>
<interceptor-binding>
<ejb-name>full.path.to.your.ejb</ejb-name>
<interceptor-class>full.path.to.your.interceptor</interceptor-class>
<method-name>method.in.your.ejb</method-name>
</interceptor-binding>
</assembly-descriptor>
I have upgraded tomcat from version 7.0.34 to version 8.0.33, and since then I have been facing a problem to share the web application context and Junit context.
I have a web application with singleton class that gathers statistic data about the web application. I also have Junit that runs the web application in embedded tomcat. the Junit queries the web application and then checks the statistic data.
I try to make a simple example:
the singleton:
public class Counter {
private static Counter instance;
private AtomicLong counter;
private Counter(){}
public static Counter getInstance(){
if(instance == null){
synchronized (Counter.class) {
if(instance == null){
instance = new Counter();
}
}
}
return instance;
}
public long incrementAndGet(){
return counter.incrementAndGet();
}
public long getValue(){
return counter.get();
}
}
the servlet:
#WebServlet(name="servlet",loadOnStartup=1, urlPatterns="/servletTest")
public class Servlet extends HttpServlet{
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hi, you are the #" + Counter.getInstance().incrementAndGet() + " visitor");
}
}
contextListener:
public class MyContextListener implements ServletContextListener{
#Override
public void contextDestroyed(ServletContextEvent arg0) {}
#Override
public void contextInitialized(ServletContextEvent arg0) {
Counter.getInstance().incrementAndGet();
}
}
Test unit:
public void mainTest() throws ServletException, LifecycleException{
Tomcat tomcat = new Tomcat();
tomcat.setPort(50000);
StandardContext ctx = (StandardContext) tomcat.addWebapp("/fe", System.getProperty("FEBaseDir")); //The FEBaseDir property is supposed to be taken from Maven build using 'test' profile
tomcat.start();
Counter.getInstance().getValue();
}
when I used Tomcat 7, everything worked fine. but since I upgraded tomcat to tomcat 8.0.33, It hasn't been working. the singleton class with the static data loads twice. first by the tomcat and then by the Junit itself.
I have tried to pass tomcat a classloader but it doesn't work.
public void mainTest() throws ServletException, LifecycleException{
Tomcat tomcat = new Tomcat();
tomcat.setPort(50000);
StandardContext ctx = (StandardContext) tomcat.addWebapp("/fe", System.getProperty("FEBaseDir")); //The FEBaseDir property is supposed to be taken from Maven build using 'test' profile
ctx.setCrossContext(true);
ctx.setLoader((Loader) new WebappLoader(Thread.currentThread().getContextClassLoader()));
ctx.setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getEngine().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getHost().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getService().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getServer().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.start();
Counter.getInstance().getValue();
}
What am I doing wrong?
You could try using the setDelegate method in StandardContext to prevent the web-app classloader from reloading the Counter class, but this impacts security in a bad manner so I advice against that.
The usual way to expose statistics is to use JMX (MBeans). You enable this by calling the setUseNaming method in StandardContext with value true.
You can register a mbean like this (copied from here):
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
mBeanServer.registerMBean(hikariPool, beanPoolName);
And you can retrieve a value like this (copied from here):
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (foo)");
HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);
int idleConnections = poolProxy.getIdleConnections();
See also this SO question and you'll probably have to read some more documentation (in my experience, it takes a while to understand the whole JMX thing and get it to work). I have not tried this in combination with unit-tests though, so YMMV.
I'm running a Vaadin Servlet with a very simple code and I want to log with Log4j when the application starts/ends, and the same for the sessions of the clients. However, only some of the messages that are logged end up being written on the log file itself.
The code of the servlet is the following:
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class, widgetset = "som.vaadin.AppWidgetSet")
public static class Servlet extends VaadinServlet implements SessionInitListener, SessionDestroyListener {
private static final Logger LOGGER = LogManager.getLogger(Servlet.class);
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
LOGGER.info("App started");
getService().addSessionInitListener(this);
getService().addSessionDestroyListener(this);
}
#Override
public void destroy() {
LOGGER.info("App stopped");
super.destroy();
}
#Override
public void sessionInit(SessionInitEvent event) {
LOGGER.info("Session started");
}
#Override
public void sessionDestroy(SessionDestroyEvent event) {
LOGGER.info("Session expired");
}
}
My log4j log configuration file is the following:
log4j.rootLogger=DEBUG, console, RollingAppender
log4j.appender.RollingAppender=org.apache.log4j.DailyRollingFileAppender
log4j.appender.RollingAppender.File=c:/temp/log/error_som.log
log4j.appender.RollingAppender.DatePattern='.'yyyy-ww
log4j.appender.RollingAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingAppender.layout.ConversionPattern= [%d{ISO8601}] %5p%6.6r[%t]%x(%F:%L) - %m%n
And finally, what I get in the log file is just:
[2015-06-09 14:17:18,197] INFO 6042 (MainBusiness.java:122) - App started
I have checked by debugging that sessionInit() is called, and the LOGGER.info() call inside it is also performed. However, the log4j Appender only received the message corresponding to the servletInitialized() function.
Any idea on what can be happening?
Thanks,
Cris
Finally, I got what was going on. Though, I cannot understand it.
The issue was caused by conflicting servlet configurations and init-params from the annotations shown in the above code and the contents of the web.xml.
The VaadinServlet was define twice, with different configurations including the configuration file for the log4j library. And sometimes, the servlet was using one and sometimes it was using the other one, even inside the same object!
So, I got it solved, but I cannot see why it was really happening.
we have a simple web application running on Tomcat 7.0.56. Now we want to use our
own realm for authentication.
public class SpecialAuth extends DataSourceRealm{
#Override
public Principal authenticate(String username, String credentials){
....
}
}
This is defined in the /META-INF/context.xml inside the war
<Context>
<Realm className="some.package.SpecialAuth" dataSourceName="jdbc/MySQL" />
</Context>
Where to put the SpecialAuth.class?
What we expected was simply to have the SpecialAuth.class inside our war but then we're getting folling exception on startup
Caused by: java.lang.ClassNotFoundException: some.package.BackOfficeAuth
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
....
If we make a jar, putting it into $TOMCAT/lib everything works fine.
But this CAN'T be the solution! That would mean every time I work on this class(es) I have to touch my tomcat server and can't use the normal deployment.
How can I use the build-in authentication mechanism without touching the tomcat all the timeß
As you said I don't like your answers :) So what I did (and I'm 100% sure that you don't like it) was to set the realm on the dirties possible way BUT now I can run it on a ontouched tomcat. After 163 acceptance tests nothing seems to break.
public final class ContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
TomcatContextManipulator tomcat = new TomcatContextManipulator(servletContext);
tomcat.applyRealm(new MyOwnRealm());
}
}
.
public class TomcatContextManipulator {
private static final String EXPEXCTED_TOMCAT_VERSION = "7.0.52.0";
private ApplicationContextFacade servletContext;
/**
* #param servletContext must be of type {#link ApplicationContextFacade}
*/
public TomcatContextManipulator(ServletContext servletContext) {
checkTomcatVersion();
ensureEquals(servletContext.getClass(), ApplicationContextFacade.class, "class of servletContext");
this.servletContext = (ApplicationContextFacade) servletContext;
}
/**
* checks if the correct version of tomcat is in use, throws {#link IllegalStateException} if not
*/
private void checkTomcatVersion() {
// we use several internal parts of tomcat (for example with reflection)
// by doing this we bind ourself hardly to a explicit version
ensureEquals(EXPEXCTED_TOMCAT_VERSION, ServerInfo.getServerNumber(), "Tomcat-Server-Version");
}
/**
* overrides the existing realm with the given on
*/
public void applyRealm(Realm realm) {
ensureNotNull(realm, "realm");
ApplicationContext applicationContext = (ApplicationContext) ReflectionUtil.get(servletContext, "context");
StandardContext standardContext = (StandardContext) ReflectionUtil.get(applicationContext, "context");
standardContext.setRealm(realm);
}
}
Note:
Reflection.get() returns the value of the (private) instance variable of the given object
ensure...() is like assert... but it throws Exception