Spring-MVC TaskScheduler - java

I want to create dynamic cron with Spring-MVC.
User can toggle cron from xhtml page. Cron works perfectly but when tomcat shutdown , i faced an error and tomcat couldnt stop properly. Tried this solution but error continues. Sharing the error message and code below (JDK 17 + Tomcat 9).
02-Feb-2023 14:17:58.960 WARNING [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [MuhurUM] registered the JDBC driver [oracle.jdbc.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
02-Feb-2023 14:17:58.961 WARNING [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MuhurUM] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.base#17.0.4/java.lang.Object.wait(Native Method)
java.base#17.0.4/java.lang.Object.wait(Object.java:338)
java.base#17.0.4/java.util.TimerThread.mainLoop(Timer.java:537)
java.base#17.0.4/java.util.TimerThread.run(Timer.java:516)
02-Feb-2023 14:17:58.962 WARNING [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MuhurUM] appears to have started a thread named [pool-3-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.base#17.0.4/jdk.internal.misc.Unsafe.park(Native Method)
java.base#17.0.4/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252)
java.base#17.0.4/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1672)
java.base#17.0.4/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182)
java.base#17.0.4/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899)
java.base#17.0.4/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062)
java.base#17.0.4/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122)
java.base#17.0.4/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
java.base#17.0.4/java.lang.Thread.run(Thread.java:833)
#Service
public class CronUtil {
#Autowired
private TaskScheduler taskScheduler;
public void scheduleATask(Long id, Runnable tasklet, String cronExpression) {
ScheduledFuture<?> scheduledTask = taskScheduler.schedule(tasklet, new CronTrigger(cronExpression, TimeZone.getTimeZone(TimeZone.getDefault().getID())));
TestApplication.jobsMap.put(id, scheduledTask);
}
public void removeScheduledTask(Long id) {
ScheduledFuture<?> scheduledTask = TestApplication.jobsMap.get(id);
if (scheduledTask != null) {
scheduledTask.cancel(true);
TestApplication.jobsMap.remove(id);
}
}
}

Related

NullPointerException when deploying to JBoss with #Schedule

I am using the wildfly maven plugin to deploy my Java app to a local jboss server.
I created a class with a #Scheduleannotation like this:
#Startup
#Singleton
#Slf4j
public class ClassName {
#Schedule(hour = "*", minute = "*", second = "*/20", persistent = false)
public void method() {
// Code
log.info("some log");
}
}
Now, when deploying I get the following error:
ERROR [org.jboss.as.ejb3.timer] (EJB default - 1) WFLYEJB0020: Error invoking timeout for timer: [id=879a76e7-b06d-458c-a722-70d8f1d40bc2 timedObjectId=xx-yy-server-1.0-SNAPSHOT.xx-yy-server-1.0-SNAPSHOT.ClassName auto-timer?:true persistent?:false timerService=org.jboss.as.ejb3.timerservice.TimerServiceImpl#42cdd9a initialExpiration=null intervalDuration(in milli sec)=0 nextExpiration=Tue Oct 15 17:21:00 CEST 2019 timerState=IN_TIMEOUT info=null]: javax.ejb.EJBException: java.lang.NullPointerException
Does anyone have any idea what is causing this? I am rather new to scheduling in Java so it might be something stupid, but I could not find anything online.
Thanks!
Hello,
so as this Stackoverflow question mentioned all the Timeout restrictions apply and more than it is recommended using #Stateless EJB instead of #Singleton when you have multiple timers that will be scheduled.
The #Startup annotation is to inform the EJB container to initialize the bean at the startup, you might be misusing it.

Shutdown service after the service has done its job

I am creating a service which watch for a file and as soon as file is available it loads it to db. Once this job is done I want to shutdown the app gracefully.
But when I use context.close it throws exception (though the app shuts down) but I want to shut it down without causing any exception.
#profile("non-test")
class Manager implements ApplicationContextAware{
#PostConstruct
public void init()
{
//watch for file and as soon as file is available invokde trigger()
}
public void trigger()
{
//load to db
shutodown()
}
public void shutdown()
{
SpringApplication.exit(context);
}
}
when i call shutdown it throws below exception.
AnnotationConfigApplicationContext has already been closed
I want the app to shutdown gracefully without any exception.
You problem is most probably presence of Spring dependency that makes your app long lived. Such dependency is for example spring-boot-starter-web. This starter includes Servlet container as dependency ans Spring will by default be running forever.
If non of such dependencies is on classpath, your Spring Boot app would shut down automatically (without any special effort to kill it). I would suggest to take a look at some of Spring guides for batch job.

SpringBatch datasource configuration - exception handling

I have a SpringBatch project where I want to catch an exception thrown when the application cannot find the datasource. I've already by-passed this, so it will use 'in memory DAO objects' instead of tables.. but it still throws an exception when datasource is not found.
I want to catch that exception and throw my own error code, but I have no idea where the try/catch block must be placed.
Here is a piece of the error log:
2016-11-24 09:25:36.171 INFO 36770 --- [main] c.d.d.e.config.ReaderConfiguration : Configuring FlatFileItemReader for [MAP]
2016-11-24 09:25:51.664 ERROR 36770 --- [main] o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initial connections of pool.
com.microsoft.sqlserver.jdbc.SQLServerException: The TCP/IP connection to the host [***], port 1433 has failed. Error: "null. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall.".
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:190) ~[sqljdbc4.jar:na]
This is overridden to bypass table creation. Also, I use two datasources and this class needed to be here anyway.
#Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {
//Spring batch needs this in order to allow to use more than one datasource
#Override
public JobRepository createJobRepository() throws Exception {
return new MapJobRepositoryFactoryBean().getObject();
}
}
To be noted that I even tried to place try/catch on "the main" method.. and it still throws the exception above.. and then gets to the breakpoint inside catch.
Also, I tried creating the datasource manually.. but to no avail. Even more, ApplicationEvent doesn't seem to work either.
This is a log where datasource is found:
2016-10-25 16:05:13 [main] INFO c.d.d.e.config.CompanyConfiguration - Configure FlatFileItemReader for [IW]
2016-10-25 16:05:13 [main] INFO o.s.jdbc.datasource.init.ScriptUtils - Executing SQL script from class path resource [org/springframework/batch/core/schema-sqlserver.sql]
2016-10-25 16:05:13 [main] INFO o.s.jdbc.datasource.init.ScriptUtils - Executed SQL script from class path resource [org/springframework/batch/core/schema-sqlserver.sql] in 49 ms.
2016-10-25 16:05:13 [main] INFO o.s.b.f.config.PropertiesFactoryBean - Loading properties file from URL [jar:file:/home/etl/d-d/d-e-1.0.4-SNAPSHOT.jar!/lib/spring-integration-core-4.2.5.RELEASE.jar!/META-INF/spring.integration.default.properties]

Spring/Hibernate adding a shutdown hook running prior to closing the EntityManagerFactory

When I close down my server, I have jobs that are populating downloading and populating data that I would like to allow to finish gracefully.
I tried to add a Runtime.getRuntime().addShutDownHook() but this seems to run after the Entitymanager is already closed, as exception starts pouring in before that gets ran.
Then I tried to add it to implement a ServletContextListener and
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent)
which ran before the shutdown hook but still after the entitymanager had already closed.
Is there a way to run some logic prior to the entitymanager shutting down?
I am injecting the entitymanager using
#PersistenceContext(unitName = PERSISTENCE_UNIT)
private EntityManager entityManager;
The EntitymanagerFactory is created using
#Configuration
#Bean(name= PERSISTENCE_UNIT)
.... createEntityManagerFactory()
How can I properly listen to when the entitymanager gets shut down, so I can allow pending work to complete first?
I am using Spring, Hibernate, JPA on Java EE with a web module.
The resulting exception is this:
java.lang.IllegalStateException: EntityManagerFactory is closed
org.hibernate.jpa.internal.EntityManagerFactoryImpl.validateNotClosed(EntityManagerFactoryImpl.java:388)
org.hibernate.jpa.internal.EntityManagerFactoryImpl.internalCreateEntityManager(EntityManagerFactoryImpl.java:342)
org.hibernate.jpa.internal.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:313)
sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:497)
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:388)
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:541)
com.sun.proxy.$Proxy51.createEntityManager(Unknown Source)
org.springframework.orm.jpa.EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactoryUtils.java:285)
org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:252)
com.sun.proxy.$Proxy53.getDelegate(Unknown Source)
It's the EntityManagerFactory that get's closed when the Spring application context is destroyed.
According to your logged exception, the main running thread has detected the application destroy event and the Spring shutdownHook propagates the destroy event to all registered beans, EntityManagerFactory included.
You probably have a batch job that has some worker threads, that don't listen to the application destroy event and therefore they continue to execute, therefore trying to create an EntityManager using a closed EntityManagerFactory.
Because the application context is destroyed, it's not just the EntityManagerFactory that is closed, but the TransactionManager and the DataSource as well. That's why there's not much you can do about it, other than discarding your current running batch jobs and simply unacknowledge the
unfinished items.
The solution is to use a persisted message queue for recording any pending work. If the server is closed, the currently processing messages shouldn't be acknowledged and so they get a chance to be reprocessed the next time you start your server.

Illegal access exception : Eclipse Persistence Localization

I'm developing a web application using JPA and Spring MVC, and I get an annoying exception about Eclipse Persistence localization. Here is the stack trace :
mars 09, 2014 2:23:06 PM org.apache.catalina.loader.WebappClassLoader findResourceInternal
Infos: Illegal access: this web application instance has been stopped already. Could not load org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource_fr.properties. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
mars 09, 2014 2:23:06 PM org.apache.catalina.loader.WebappClassLoader loadClass
Infos: Illegal access: this web application instance has been stopped already. Could not load org.eclipse.persistence.internal.localization.i18n.LoggingLocalizationResource_fr_FR. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1600)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
at java.util.ResourceBundle$Control.newBundle(ResourceBundle.java:2566)
at java.util.ResourceBundle.loadBundle(ResourceBundle.java:1436)
at java.util.ResourceBundle.findBundle(ResourceBundle.java:1400)
at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1296)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:795)
at org.eclipse.persistence.internal.localization.EclipseLinkLocalization.buildMessage(EclipseLinkLocalization.java:60)
at org.eclipse.persistence.internal.localization.EclipseLinkLocalization.buildMessage(EclipseLinkLocalization.java:34)
at org.eclipse.persistence.internal.localization.LoggingLocalization.buildMessage(LoggingLocalization.java:25)
at org.eclipse.persistence.logging.AbstractSessionLog.formatMessage(AbstractSessionLog.java:987)
at org.eclipse.persistence.logging.DefaultSessionLog.log(DefaultSessionLog.java:142)
at org.eclipse.persistence.internal.sessions.AbstractSession.log(AbstractSession.java:3480)
at org.eclipse.persistence.internal.sessions.AbstractSession.log(AbstractSession.java:4669)
at org.eclipse.persistence.internal.sessions.AbstractSession.log(AbstractSession.java:4641)
at org.eclipse.persistence.internal.sessions.AbstractSession.log(AbstractSession.java:4617)
at org.eclipse.persistence.internal.sessions.AbstractSession.log(AbstractSession.java:4539)
at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.logout(DatabaseSessionImpl.java:937)
at org.eclipse.persistence.sessions.server.ServerSession.logout(ServerSession.java:776)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.removeSessionFromGlobalSessionManager(EntityManagerSetupImpl.java:511)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.undeploy(EntityManagerSetupImpl.java:2850)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.close(EntityManagerFactoryDelegate.java:267)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.finalize(EntityManagerFactoryDelegate.java:344)
at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)
at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:101)
at java.lang.ref.Finalizer.access$100(Finalizer.java:32)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:190)
As I'm new to JPA and Spring MVC, I don't know what causes this exception. Why do I have this exception and how to solve it ? Thanks.
I've setup my Eclipse WTP server to "Automatically publish after a build event". After each automatic publish I've encountered the same error messages as You have shown.
There are two ways to solve this issue.
Turn your Eclipse Server Setting to "Never publish automatically".
Add a Servlet Shutdown Hook, which does close the JPA's EntityManagerFactory explicitly.
You can add a shotdown hook easily to a ServletContextListener. In my case this is a GuiceServletContextListener, but it should be valid for any ServletContextListener.
import javax.persistence.EntityManagerFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.annotation.WebListener;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
#WebListener
public class BmjServletContextListener extends GuiceServletContextListener {
Injector injector;
public void contextDestroyed(ServletContextEvent servletContextEvent) {
injector.getInstance(EntityManagerFactory.class).close(); // <--- POI !!
super.contextDestroyed(servletContextEvent);
}
public void contextInitialized(ServletContextEvent servletContextEvent) {
super.contextInitialized(servletContextEvent);
}
protected Injector getInjector() {
injector = Guice.createInjector(new MyModule1(), new MyModule2());
return injector;
}
}

Categories

Resources