I have a Spring boot application with several camel routes that should start based on a quartz2's CronTrigger. For some reason, only the the route scheduled first is ever started, but it starts at the time scheduled for the last route.
route one: mytime - 1h
route two: mytime
Only route one is started, at mytime.
I have made a minimal example. Because my routes are supposed to check the contents of a database table and export part of it, in my example the routes will check the table and log the most recent date found in a column set in the properties.
Routebuilder:
/**
* Starts a list of routes that have been scheduled in application.yml
*/
#Component
public class ScheduledRoutesRouteBuilder extends SpringRouteBuilder {
private static final Logger LOG = LoggerFactory.getLogger(ScheduledRoutesRouteBuilder.class);
private static final String BEAN_CHECKDB = "bean:checkDBBean?method=getFirstRecord(%s, %s)";
#Autowired
private RoutesDefinition routesDefinition;
#Override
public void configure() throws Exception {
routesDefinition.getScheduledRoutes().stream()
.forEach(route -> createScheduledRoute(route));
}
private void createScheduledRoute(RouteDefinition aRoute) {
from(aRoute.getSchedule())
.routeId(aRoute.getRouteId())
.log(LoggingLevel.INFO, LOG, "Kickstarting export route: " + aRoute.getRouteId() + " - schedule: " + aRoute.getSchedule())
.to(String.format(BEAN_CHECKDB, aRoute.getDbTableName(), aRoute.getReferenceDateColumnName()));
System.out.println("Configured export route: " + aRoute.getRouteId() + " - schedule: " + aRoute.getSchedule());
}
}
application.yml:
# Schedules
scheduleFirst: 0 39 * * * ?
scheduleSecond: 0 41 * * * ?
scheduledRoutes:
- routeId: MonthProcessingRoute
dbTableName: month
referenceDateColumnName: acceptatiedatum
schedule: quartz2://CronTrigger?cron=${scheduleFirst}
- routeId: WeekProcessingRoute
dbTableName: week
referenceDateColumnName: acceptatiedatum
schedule: quartz2://CronTrigger?cron=${scheduleSecond}
Log:
Configured export route: MonthProcessingRoute - schedule:
quartz2://CronTrigger?cron=0 39 * * * ?
Configured export route: WeekProcessingRoute - schedule: quartz2://CronTrigger?cron=0 41 * * * ?
2018-03-20 05:37:33 INFO tryouts-spring-camel ivana.StartUp - -
- Started StartUp in 2.507 seconds (JVM running for 3.238)
2018-03-20 05:41:00 INFO tryouts-spring-camel ivana.routebuilders.ScheduledRoutesRouteBuilder - - - Kickstarting
export route: MonthProcessingRoute - schedule:
quartz2://CronTrigger?cron=0 39 * * * ?
Most recent date found in database table month: 2017-11-05 15:31:00.0
You should make sure to use unique triggerName/groupName for each of your Camel routes. It looks like you use the same name CronTrigger in both routes. Change that to be unique names, and it should work.
I have a scenario where I have multiple advice being applied on a single joint point because of the common point cut expression. Is there any way through which I can log which advice is currently executing or being executed without giving them different log statements(by means of some method invocations)?
/*
* Advice to check Asset Service 1 response
*/
#Around(value="#annotation(vs2a) && args(mfa)")
public MessageFlowAggregator checkAsset1Response(ProceedingJoinPoint joinPoint,ValidateStage2Advice vs2a,MessageFlowAggregator mfa) throws Throwable {
log.debug(">>> matching advice on {}",joinPoint);
if(mfa!=null){
mfa= (MessageFlowAggregator) joinPoint.proceed();
log.debug("<<< returning advice on {}",joinPoint);
}else{
log.debug("<<< failing advice on {}",joinPoint);
}
return mfa;
}
/*
* Advice to check Customer Service 2 response
*/
#Around(value="#annotation(vs2a) && args(mfa)")
public MessageFlowAggregator checkCustomer2Response(ProceedingJoinPoint joinPoint,ValidateStage2Advice vs2a,MessageFlowAggregator mfa) throws Throwable {
log.debug(">>> matching advice on {}",joinPoint);
if(mfa!=null){
mfa= (MessageFlowAggregator) joinPoint.proceed();
log.debug("<<< returning advice on {}",joinPoint);
}else{
log.debug("<<< failing advice on {}",joinPoint);
}
return mfa;
}
Both the above advice print s same log statement and I am not able to differentiate between them.
Thanks in advance !
Well, you could just add a prefix to each log message. The alternative would be:
log.debug("Executing advice: " + new Exception().getStackTrace()[0])
And of course if you print the same thing twice from within one advice, you should store it in a variable first so as not to make creating exception objects and stacktraces even more expensive. But I think for debugging it should be okay.
I think it's better that we use logging frameworks logging pattern to get the method name in every log printed. I update the logging pattern to get the desired output in log.
Pattern:
logging.pattern.console=%d %-5level %t %logger{2}.%M : %msg%n
logs printed as:
2018-02-01 19:17:39,798 INFO main o.s.w.s.s.SaajSoapMessageFactory.afterPropertiesSet : Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
2018-02-01 19:17:39,798 INFO main o.s.w.s.s.SaajSoapMessageFactory.afterPropertiesSet : Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
2018-02-01 19:17:39,798 INFO main o.s.w.s.s.SaajSoapMessageFactory.afterPropertiesSet : Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
2018-02-01 19:17:39,798 INFO main o.s.w.s.s.SaajSoapMessageFactory.afterPropertiesSet : Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
I'm using NetBeans and Tomcat seems to be deploying in .war application twice, a double launch of the web app.
I've tried both Tomcat 6 and 7 and the same result.
I've got a Spring MVC, Hibernate and Thymeleaf application.
Context.xml under META-INF has the following content:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/website"/>
Here is the log.
**First deployment starts**
[ INFO] 07:13:09 ContextLoader - Root WebApplicationContext: initialization started
[ INFO] 07:13:09 XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Thu May 23 07:13:09 EST 2013]; root of context hierarchy
2013-05-23 07:13:10 JRebel: Monitoring Spring bean definitions in '/Users/pack/NetBeansProjects/mysite/site/src/main/webapp/WEB-INF/applicationContext- data.xml'.
[ INFO] 07:13:10 XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/applicationContext-data.xml]
[ INFO] 07:13:10 ClassPathScanningCandidateComponentProvider - JSR-330 'javax.inject.Named' annotation found and supported for component scanning
***(tomcat initializes hibernate and other spring beans)***
...
May 23, 2013 7:13:17 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 15552 ms
***Tomcat started***
***Tomcat tries to shut down the context***
[ INFO] 07:13:18 XmlWebApplicationContext - Closing WebApplicationContext for namespace 'spring-mvc-servlet': startup date [Thu May 23 07:13:15 EST 2013]; parent: Root WebApplicationContext
[ INFO] 07:13:18 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#5bbe2de2: defining beans [blHeadProcessor,blHeadProcessorExtensionManager,navigationProcessor,blPaginationPageLinkPro cessor,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,blRegisterCustomerValidator,blCategoryController,com.package.ui.thymeleaf.CategoryHandlerMapping#0,templateResolver,templateEngine,org.thymeleaf.spring3.view.ThymeleafViewResolver#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory#521e7f21
[ INFO] 07:13:18 XmlWebApplicationContext - Closing Root WebApplicationContext: startup date [Thu May 23 07:13:09 EST 2013]; root of context hierarchy
[ INFO] 07:13:18 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#521e7f21: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0,blCategoryDao,blCustomerDao,blIdGenerationDao,nlpDao,jpaTemplate,webDS,entityManagerFactory,transactionManager,org.springframework.security.filterChains,org.springframework.security.filterChainProxy,org.springframework.security.web.DefaultSecurityFilterChain#0,org.springframework.security.web.DefaultSecurityFilterChain#1,org.springframework.security.web.DefaultSecurityFilterChain#2,org.springframework.security.web.DefaultSecurityFilterChain#3,org.springframework.security.web.DefaultSecurityFilterChain#4,org.springframework.security.web.DefaultSecurityFilterChain#5,org.springframework.security.web.PortMapperImpl#0,org.springframework.security.web.PortResolverImpl#0,org.springframework.security.authentication.ProviderManager#0,org.springframework.security.web.context.HttpSessionSecurityContextRepository#0,org.springframework.security.web.savedrequest.HttpSessionRequestCache#0,org.springframework.security.web.access.channel.ChannelDecisionManagerImpl#0,org.springframework.security.access.vote.AffirmativeBased#0,org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0,org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator#0,org.springframework.security.authentication.AnonymousAuthenticationProvider#0,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0,org.springframework.security.userDetailsServiceFactory,org.springframework.security.web.DefaultSecurityFilterChain#6,org.springframework.security.authentication.dao.DaoAuthenticationProvider#0,org.springframework.security.authentication.DefaultAuthenticationEventPublisher#0,org.springframework.security.authenticationManager,blUserDetailsService,blCatalogService,blCustomerService,entityService,fbPageService,blIdGenerationService,blLoginService,nlpService,priceIncreaseValidator,searchFacetService,blEntityConfiguration,blPasswordEncoder,solrIndexService,solrEmbedded,textEncryptor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
[ INFO] 07:13:18 LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'blPU'
[ INFO] 07:13:18 SessionFactoryImpl - closing
May 23, 2013 7:13:18 AM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/website] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
May 23, 2013 7:13:19 AM org.apache.catalina.startup.HostConfig deleteRedeployResources
INFO: Undeploying context [/website]
May 23, 2013 7:13:19 AM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor /Users/pack/Servers/apache-tomcat- 7.0.34/conf/Catalina/localhost/website.xml
2013-05-23 07:13:23 JRebel: Monitoring Log4j configuration in 'file:/Users/pack/NetBeansProjects/mysite/site/target/mycompany/WEB-INF/classes/log4j.xml'.
***Tomcat tries to restart again***
[ INFO] 07:13:23 ContextLoader - Root WebApplicationContext: initialization started
[ INFO] 07:13:23 XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Thu May 23 07:13:23 EST 2013]; root of context hierarchy
2013-05-23 07:13:24 JRebel: Monitoring Spring bean definitions in '/Users/pack/NetBeansProjects/mysite/site/src/main/webapp/WEB-INF/applicationContext- data.xml'.
[ INFO] 07:13:24 XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/applicationContext-data.xml]
and it start successfully.
What I don't understand is why is Tomcat is deploying the application twice.
This does not occur the first time I deploy the application on a new tomcat instance because the website.xml file is not in tomcats /conf/catalina/localhost folder yet. But when I stop and run tomcat from netbeans again the website.xml file is still in the /conf/catalina/localhost folder but get deleted and re-deployed just before the second deployment is about to happen.
I've tried setting autoDeploy in server.xml file of Tomcat to false but it didn't help.
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="false">
May be Tomcat should be deleting the website.xml file under /conf/catalina/localhost folder everytime tomcat stops.
This is how website.xml file under localhost folder looks like
<?xml version="1.0" encoding="UTF-8"?>
<Context
docBase="/Users/pack/NetBeansProjects/mysite/site/target/mycompany"
path="/website"
/>
I found that deleting the file conf/localhost/myappname.xml prevents the app from initializing twice. Basically Tomcat is restarting, and restarting the old version of your app. Then it starts again when Netbeans deploys it. As a workaround, I added a few lines of code in my ContextListener contextDestroyed() event:
public void contextDestroyed(ServletContextEvent sce) {
...
String delme = sce.getServletContext().getInitParameter("eraseOnExit");
if (delme != null && delme.length() > 0) {
File del = new File(delme);
if (del.exists()) {
System.out.println("Deleting file " + delme);
del.delete();
}
}
In the web.xml add the following in a dev environment:
<context-param>
<description>Workaround for Tomcat starting webapp twice</description>
<param-name>eraseOnExit</param-name>
<param-value>/Users/xxx/apache-tomcat-7.0.42/conf/Catalina/localhost/myappname.xml</param-value>
</context-param>
Then the next time the app is deployed, it won't be started again prior to the deployment, hence will not start twice. Any other ideas for deleting a file prior to deploying, or on shutdown, would be appreciated.
Thanks for the answer by epoch and the answer by Steven Neiner.
Here is my version of their code. My differences:
Marked method as synchronized.
Theoretically not needed, but given that we are dealing with weird multiple launching problem here it is better to be safe than sorry.
Replaced calls to third-party utility (Spring?).
Detect if running in development by looking for certain wording in the catalina.base path.
Deleted the static modifier. Using a Singleton implemented as an enum.
As of 2016-05, rewrote the code to be easier to read and comprehend (at least for me). Only briefly tested, so be sure to review the source code before using (and must be used entirely at your own risk).
Concept
The core of their workaround to this bug is to delete the file named with the name of your web app (your “servlet context”) and appended with .xml.
For example, if your web app is named AcmeApp, locate and delete the file named AcmeApp.xml. This file is stored nested within the “Catalina base” folder.
Do this deletion as the very last step of your web app’s run. So when the web app launches again, that file will not exist, and will be recreated. Remember this is only in development mode. The bug does not occur when using Tomcat on its own in production.
So how do we run this workaround code as the last act of our web app’s execution? Read on.
How To Use
As a standard part of version 2.3 and later of the Servlet spec, every Servlet container has hooks to call your code when your web launches and again when your web app is being shut down. This is not Tomcat specific; Jetty, GlassFish, WildFly/JBoss, and so on, all include this feature as required by the Servlet spec.
To use the code shown above, add a new class to your project. Name the new class something like "MyServletContextListener.java". Declare that class as implementing the ServletContextListener interface.
Implement the two methods required by this interface. One method is called by your Servlet container (Tomcat) when your web app launches, guaranteed to run before the first user hits your app. The other method is called when your web app is being shut down by the Servlet container (Tomcat).
In the contextDestroyed method, call the method shown above. Like this:
#Override
public void contextInitialized ( ServletContextEvent sce )
{
// Web app launching.
// This method runs *before* any execution of this web app’s servlets and filters.
// Do nothing. No code needed here.
}
#Override
public void contextDestroyed ( ServletContextEvent sce )
{
// Web app shutting down.
// This method runs *after* the last execution of this web app’s servlets and filters.
// Workaround for NetBeans problem with launching Tomcat twice.
this.workaroundTomcatNetbeansRedeployBug( sce );
}
The configuration is easy. Merely include this class alongside your servlet class it a WAR file/folder. The #WebListener annotation causes the Servlet container to “notice” this listener class, load and instantiate it, and when appropriate execute each of its methods. You may use alternate modes of configuration instead of the annotation if needed, but the annotation is the simplest route.
Here is an entire AppListener class as a full example. I have re-written the previously posted version of this code to be easier to read and comprehend.
package com.basilbourque;
import java.io.File;
import java.io.FilenameFilter;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* Hooks into the web app launching and quitting, as a workaround for Tomcat
* running from NetBeans causing the web app to rapidly deploy, undeploy and
* redeploy.
*
* © 2016 Basil Bourque. This source code may be used freely, and entirely at
* your own risk, according to terms of the ISC License at:
* https://opensource.org/licenses/ISC
*
* #author Basil Bourque
*/
#WebListener
public class AppListener implements ServletContextListener {
#Override
public void contextInitialized ( final ServletContextEvent servletContextEventArg ) {
System.out.println ( "Basil launch" );
}
#Override
public void contextDestroyed ( final ServletContextEvent servletContextEventArg ) {
System.out.println ( "Basil exit" );
this.workaroundTomcatNetbeansRedeployBug ( servletContextEventArg );
}
synchronized private void workaroundTomcatNetbeansRedeployBug ( final ServletContextEvent servletContextEventArg ) {
// When running Tomcat 8 from NetBeans 8, as we do in development, a bug causes the web app to rapidly deploy, undeploy, and redeploy.
// This bug causes multiple bad side-effects.
//
// Workaround: When running in development mode with NetBeans & Tomcat, delete the XML file with name of web app, found in {catalina-base}/conf/Catalina/localhost/YourWebAppNameHere.xml.
// Example of file name to delete: If your app is named “AcmeApp”, then in a Vaadin multi-module Maven archetype app, the target file might be named “AcmeApp-ui.xml”.
// In a simpler project, the target file might be named “AcmeApp.xml”.
// So we need to determine the name of the web app in a soft-coded fashino.
// We extract from a context path. For example, '/AcmeApp-ui'. We need to remove that slash (SOLIDUS) at the front.
// Then we append a “.xml” to create our target file name.
// We look for that file in the folder nested in the Cataline base folder (see line above for path).
// If file is found, add it to the list of files to be deleted. That list will have only one element.
// Lastly, delete the file.
if ( AppUtility.INSTANCE.isInDevelopmentMode () ) { // Find a strategy to determine if you are in development mode.
final String catalinaBase = System.getProperty ( "catalina.base" );// Path to the folder the working folder of this web app.
final String contextPath = servletContextEventArg.getServletContext ().getContextPath ();
final String contextName = contextPath.substring ( 1 ); // Strip the SOLIDUS (slash) from first character position. Example: '/AcmeApp-ui' becomes 'AcmeApp-ui'.
final String fileNameToDelete = contextName + ".xml";
final File catalinaBaseContext = new File ( catalinaBase , "conf/Catalina/localhost" ); // While in development, running Tomcat from NetBeans, the web app’s name is 'localhost'.
if ( catalinaBaseContext.exists () && catalinaBaseContext.canRead () ) { // Confirm that we found the expected configuration folder nested in Catalina’s 'base' folder.
// Make an array of File objects that match our criterion of having one of our expected file names.
// Populate this array by defining a filter of filenames via a functional interface, to be applied against each file found in folder.
final File[] filesToDelete = catalinaBaseContext.listFiles ( new FilenameFilter () {
#Override
public boolean accept ( File dir , String name ) {
boolean accepting = ( name.equals ( fileNameToDelete ) );
return accepting;
}
} );
if ( filesToDelete.length == 0 ) { // If list of files is empty…
// FIXME Handle error. Should always find one file to delete.
System.out.println ( "ERROR - Found no file to delete as workaround for NetBeans+Tomcat double-launch bug. Expected file name: " + fileNameToDelete + " | Message # 42ec5857-9c1b-431a-b5c1-2588669a0ee2." );
return;
}
if ( filesToDelete.length > 1 ) { // If list of files has more than one file…
// FIXME Handle error. Should never find more than one file to delete.
System.out.println ( "ERROR - Found more than one file to delete as workaround for NetBeans+Tomcat double-launch bug." + " | Message # 0afbd6ca-3722-4739-81dc-b2916e9dbba4." );
return;
}
for ( File file : filesToDelete ) {
file.delete (); // Delete first file found in our filtered array.
// FIXME You may want to log this deletion.
System.out.println ( "TRACE - Deleting file as workaround for NetBeans+Tomcat double-launch bug: " + file + " | Message # 5a78416c-6653-40dc-a98c-6d9b64766d96." );
break; // Should be exactly one element in this list. But out of abundant caution, we bail-out of the FOR loop.
}
}
}
}
}
And here is the helper class to determine if running in development mode. Read the comments for more discussion. The upshot is that there seems to be no simple clean way to detect when in development, no way to detect when running Tomcat from NetBeans rather than running Tomcat on its own. I have asked but have not received any better solution.
CAVEAT: You must alter this isInDevelopmentMode method to match your particular development environment.
package com.basilbourque;
import java.util.ArrayList;
import java.util.List;
/**
* Detects if this web app is running in the Apache Tomcat web container from
* within NetBeans during development time.
*
* © 2016 Basil Bourque. This source code may be used freely, and entirely at
* your own risk, according to terms of the ISC License at:
* https://opensource.org/licenses/ISC
*
* #author Basil Bourque.
*/
public enum AppUtility {
INSTANCE;
transient private Boolean isDevMode;
synchronized public Boolean isInDevelopmentMode () {
// There is no simple direct way to detect if running in development.
// As a workaround, I use some facts specific to my running Tomcat from NetBeans while developing.
//
// The “Catalina base” is the folder used by Tomcat’s Catalina module to do the work of your servlets.
// The names of the folders in the path to that folder can be a clue about running in development.
//
// By default, the Catalina base folder is nested within Tomcat’s own folder.
//
// If you run NetBeans with a bundled Tomcat installation that path may contain the word “NetBeans”.
// At least this is the case on Mac OS X where that bundled Tomcat is stored within the NetBeans app (an app is actually a folder in Mac OS X).
//
// You mant to create your own folder to hold Tomcat’s “base” folder.
// I do this on my development machine. I create a folder named something like "apache-tomcat-base-dev" in my home folder.
// Nested inside that folder are additional folders for each version of Tomcat I may be using, such as 'base-8.0.33'.
// Since I do not use such a name on my production environment, I can example the path for that phrasing to indicate development mode.
//
if ( null == this.isDevMode ) { // Lazy-loading.
// Retrieve the folder path to the current Catalina base folder.
String catalinaBaseFolderPath = System.getProperty ( "catalina.base" );
this.isDevMode = Boolean.FALSE;
// Examine that path for certain wording I expect to occur only in development and never in production.
List<String> list = new ArrayList<> ();
list.add ( "Application Support" );// Specific to Mac OS X only.
list.add ( "NetBeans" );
list.add ( "apache-tomcat-base-dev" ); // My own name for an external folder to keep Catalina base separate, outside of NetBeans and Tomcat.
for ( String s : list ) {
if ( catalinaBaseFolderPath.contains ( s ) ) {
this.isDevMode = Boolean.TRUE;
break; // Bail-out of the FOR loop after first hit.
}
}
}
return this.isDevMode;
}
}
First of all, thanks Steven! Here is a more portable version of the fix:
/**
* tomcat workaround bug, in development mode, if tomcat is stopped and application is not un-deployed,
* the old application will start up again on startup, and then the new code will be deployed, leading
* to a the app starting two times and introducing subtle bugs, when this app is stopped and in dev mode
* remove the deployment descriptor from catalina base
*/
private static void preventTomcatNetbeansRedeployBug(final ServletContextEvent sce) {
final String contextPath = sce.getServletContext().getContextPath();
final String catalinaBase = System.getProperty("catalina.base");
if (StringUtil.checkValidity(contextPath, catalinaBase)
&& FrameworkContext.getInstance().isDevEnvironment()) {
final File catalinaBaseContext = new File(catalinaBase, "conf/Catalina/localhost");
if (catalinaBaseContext.exists() && catalinaBaseContext.canRead()) {
final File[] contexts = catalinaBaseContext.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.equals(contextPath.substring(1) + ".xml");
}
});
if (contexts != null && contexts.length > 0) {
LOG.info("Deleting core context[" + contexts[0].getAbsolutePath() + "] since we are in dev");
contexts[0].delete();
}
}
}
}
PS: substitute unknown references for your own version of it :)
Call this custom method from the contextDestroyed method of your ServletContextListener implementation.
In my case it was (after clicking 'run' in NetBeans):
deploy app in Tomcat (unnecessary)
undeploy app in Tomcat (unnecessary)
build app
deploy app in Tomcat
And I fixed that issue simply by cleaning project before running it - 'clean' -> 'run' :)
Most of my app logging is done at debug level because in sf 1.4 it's not used by symfony itself, and that makes it easy to see just the messages I'm interested in using something like:
tail -f log/frontend_dev.php | grep "\[debug\]"
This is great in the dev environment while I'm sat there watching it scroll past, but I now want to log only these debug messages in the production environment.
If I set the log level for the production log to debug, then obviously I'll get everything down to and including the debug level, which is far too much data.
Is it possible to write a logger that will just record [debug] messages and nothing else?
Of course it is possible - you can extend sfFileLogger and override log($message, $priority) function (which sfFileLogger inherits from sfLogger class).
...
public function log($message, $priority = self::DEBUG)
{
if ($this->getLogLevel() != $priority)
{
return false;
}
return $this->doLog($message, $priority);
}
...
Now you have to configure logging in your app to use your new logger class, configuration is located in app/<your app>/config/factories.yml:
prod:
logger:
class: sfAggregateLogger
param:
level: DEBUG
loggers:
sf_file_debug:
class: myDebugOnlyLoggerClass
param:
level: DEBUG
file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%.log
This logger will save only messages logged with the same priority (and only the same, not the same or higher) as configured in factories.yml.