I'm trying to rewrite an application using Spring Boot 2 (specifically 2.1.5.RELEASE). Previous version used 1.5.7.RELEASE. We are switching database platforms, thus the rewrite. The startup class in both new and old are nearly identical, both attempting to create a bean from another library. It works in 1.5.7, but in 2.1.5, it fails with the following message:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process
import candidates for configuration class [com.company.consumer.ConsumerRecoveryConfiguration];
nested exception is java.lang.IllegalStateException: Failed to introspect
annotated methods on class com.company.consumer.RecordRecovery
Here's what the startup class looks like in old and new:
Old:
#SpringBootApplication
#ComponentScan(basePackages = {
"com.company.core",
"com.company.something",
"com.company.platform",
"com.company.config"},
excludeFilters = #ComponentScan.Filter(type =
FilterType.REGEX, pattern = "com.company.common.diag.*"))
#Import({SomeClass.class, ConsumerRecoveryConfiguration.class,
ConsumerRetryConfiguration.class})
#PropertySource("classpath:build.properties")
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
#Bean(name = "someDbTimingLogger")
public TimingLogger getSomeDbTimingLogger() {
return new TimingLogger(LoggerFactory.getLogger("timing.someDb"));
}
#Bean(name = "timingLogger")
public TimingLogger getTimingLogger() {
return new TimingLogger(LoggerFactory.getLogger("timing.consumer"));
}
#Bean(name = "kafkaTimingLogger")
public TimingLogger getKafkaTimingLogger() {
return new TimingLogger(LoggerFactory.getLogger("timing.kafka"));
}
#Bean(name = "parser")
public Parser<GenericMessageModel> getSomeModelParser() {
return new Parser<>(GenericMessageModel.class);
}
}
New:
#SpringBootApplication
#ComponentScan(basePackages = {
"com.company.core",
"com.company.something",
"com.company.platform",
"com.company.config"},
excludeFilters = #ComponentScan.Filter(
type = FilterType.REGEX,
pattern = "com.company.common.diag.*"
))
#Import({ ConsumerRecoveryConfiguration.class,
ConsumerRetryConfiguration.class })
#PropertySource("classpath:build.properties")
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
#Bean(name = "timingLogger")
public TimingLogger getTimingLogger() { return new TimingLogger(LoggerFactory.getLogger("timing.consumer")); }
#Bean(name = "kafkaTimingLogger")
public TimingLogger getKafkaTimingLogger() { return new TimingLogger(LoggerFactory.getLogger("timing.kafka")); }
#Bean(name = "parser")
public Parser<GenericMessageModel> getSomeModelParser() { return new Parser<>(GenericMessageModel.class, null); }
}
The Kotlin class mentioned in the nested exception is as follows (minus its numerous private methods):
#Component
class RecordRecovery(val producer: KafkaProducer<String, String>, val kafkaProducerSettings: KafkaProducerSettings,
val recoverySettings: RecoverySettings, val objectMapper: ObjectMapper) {
val random = ThreadLocalRandom.current()!!
companion object {
val LOGGER = LoggerFactory.getLogger(RecordRecovery::class.java)
}
fun recoverRecords(records: ConsumerRecords<String, String>, exception: Exception) {
recoverRecords(records.map { it.value() ?: StringUtils.EMPTY }, exception)
}
fun recoverRecords(records: List<Any>, exception: Exception) {
error(metaDataFor(records.count()), LOGGER, "Recovering {} records", records.count())
val filteredRecords = records.map { createRecoveryRecord(it) }
.filter{ it != null }
.map { recordToJson(it) }
.filter { removeNulls(it) }
.mapIndexed { index, it -> toEmergencyDataWrapper(it!!, exception, index) }
.mapNotNull {toJSONStringOrNull(it)}
.map {toProducerRecord(it)}
.map { republish(it) }
.toList()
info(metaDataFor(filteredRecords.count()), LOGGER, "After filtering, recovering {} records", filteredRecords.count())
filteredRecords.map { await(it) }
}
}
And here's the ConsumerRecoveryConfiguration class (also Kotlin) in question:
#Configuration
#Import(KafkaProducerConfig::class, RecoverySettings::class, RecordRecovery::class)
class ConsumerRecoveryConfiguration
The class has no body whatsoever. I'm assuming that importing the other classes causes Spring to basically use those as the body.
I'm still somewhat green when it comes to Spring, so I'm struggling to see why it stopped working when I changed to SB 2. Any suggestions about where I should look or what might help me figure this out?
EDIT:
Here is the full stacktrace as requested:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.company.consumer.ConsumerRecoveryConfiguration]; nested exception is java.lang.IllegalStateException: Failed to introspect annotated methods on class com.company.consumer.RecordRecovery
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:596)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:302)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:242)
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:586)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:302)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:242)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:315)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:705)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:531)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.company.SomeApplication.main(SomeApplication.java:35)
Caused by: java.lang.IllegalStateException: Failed to introspect annotated methods on class com.company.consumer.RecordRecovery
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:169)
at org.springframework.context.annotation.ConfigurationClassParser.retrieveBeanMethodMetadata(ConfigurationClassParser.java:392)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:317)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:242)
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:586)
... 20 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/json/JSONObject
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:158)
... 24 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.json.JSONObject
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 28 common frames omitted
Related
After upgrading to embeded MongoDB with version 3.0.0, I am getting the following exception.
I am using the following in build.gradle.
testImplementation group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.0.0'
The exception is given below.
Caused by: java.lang.ClassNotFoundException: de.flapdoodle.embed.process.config.IRuntimeConfig
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_261]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_261]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[na:1.8.0_261]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_261]
at java.lang.Class.forName0(Native Method) ~[na:1.8.0_261]
at java.lang.Class.forName(Class.java:348) ~[na:1.8.0_261]
at org.springframework.util.ClassUtils.forName(ClassUtils.java:284) ~[spring-core-5.2.10.RELEASE.jar:5.2.10.RELEASE]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:324) ~[spring-core-5.2.10.RELEASE.jar:5.2.10.RELEASE]
I provide below the code. I want to use embeded mongoDB for the MongoDB transaction feature in the integration test in spring boot.
#Profile("test")
#ActiveProfiles("test")
public class TestMongoConfig1 implements InitializingBean, DisposableBean {
MongodForTestsFactory factory = null;
MongodConfig mongodConfig = MongodConfig.builder().version(Version.Main.PRODUCTION).build();
MongodStarter runtime = MongodStarter.getDefaultInstance();
MongodExecutable mongodExecutable = null;
MongodProcess mongod = null;
#Override
public void destroy() throws Exception {
mongodExecutable.stop();
}
#Override
public void afterPropertiesSet() throws Exception {
mongodExecutable = runtime.prepare(mongodConfig);
mongod = mongodExecutable.start();
}
#Bean(name = "test1")
public MongoClient mongoClient() {
MongoClient mongoClient = MongoClients.create();
System.out.println("============================================");
System.out.println(mongoClient);
System.out.println("============================================");
return mongoClient;
}
}
First you have to use the following dependencies in build.gradle apart from other dependencies if you are using #DataMongoTest.
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
testImplementation("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
To test with embeded MongoDB, you have to use the following snippet which may work.
#Profile("test")
#ActiveProfiles("test")
#Configuration
public class TestMongoConfig1 implements InitializingBean, DisposableBean {
private MongodExecutable executable;
#Override
public void afterPropertiesSet() throws Exception {
IFeatureAwareVersion version = Versions.withFeatures(new GenericVersion("4.0.0"),
Version.Main.PRODUCTION.getFeatures());
IMongoCmdOptions cmdOptions = new
MongoCmdOptionsBuilder().useNoPrealloc(false).useSmallFiles(false)
.master(false).verbose(false).useNoJournal(false).syncDelay(0).build();
int port = Network.getFreeServerPort();
IMongodConfig mongodConfig = new MongodConfigBuilder().version(version)
.net(new Net(port, Network.localhostIsIPv6())).replication(new Storage(null, "testRepSet", 5000))
.configServer(false).cmdOptions(cmdOptions).build();
MongodStarter starter = MongodStarter.getDefaultInstance();
executable = starter.prepare(mongodConfig);
executable.start();
}
I am facing some difficulties in writing junit test to pass the for loop condition to getParts() method from SlingHttpServletRequest.getParts(). There is no problem with the implementation, I am able to process the file attachment properly. However, I am unable to do so in the junit test.
The following is my implementation:
#Model(adaptables = SlingHttpServletRequest.class)
public class Comment {
//Variables declaration
#Inject
private CommentService service;
#PostConstruct
public void setup() {
requestData = new JSONObject();
for (String item : request.getRequestParameterMap().keySet()) {
try {
requestData.put(item, request.getParameter(item));
}
} catch (Exception e) {
Throw error message
}
}
//Upload attachment to server
try {
for (Part part : request.getParts()) { <= The JUnit test stopped at this line and throw the error below
} catch (Exception e) {
Throw error message
}
I have tried using a SlingHttpServletRequestWrapper class to override the getParts method but to no avail.
The following is my junit test:
public class CommentTest {
public final AemContext context = new AemContext();
private CommentService commentService = mock(CommentService.class);
#InjectMocks
private Comment comment;
private static String PATH = "/content/testproject/en/page/sub-page";
#Before
public void setUp() throws Exception {
context.addModelsForPackage("de.com.adsl.sightly.model");
context.load().json("/components/textrte.json", PATH);
context.currentPage(PATH);
}
#Test
public void testSetup() throws IOException, ServletException {
//before
context.request().setParameterMap(getRequestCat1());
context.registerService(CommentService.class, commentService);
Resource resource = context.resourceResolver().getResource(PATH + "/jcr:content/root/responsivegrid/textrte");
assertNotNull(resource);
//when
comment = new CustomRequest(context.request()).adaptTo(Comment.class);
//then
comment.setup();
}
private class CustomRequest extends SlingHttpServletRequestWrapper {
public CustomRequest(SlingHttpServletRequest request) {
super(request);
}
#Override
public Collection<Part> getParts() {
final String mockContent =
"------WebKitFormBoundarycTqA2AimXQHBAJbZ\n" +
"Content-Disposition: form-data; name=\"key\"\n" +
"\n" +
"myvalue1\n" +
"------WebKitFormBoundarycTqA2AimXQHBAJbZ";
final List<Part> parts = MockPart.parseAll(mockContent);
assertNotNull(parts);
return parts;
}
};
}
The following is the error message that I encountered:
14:53:04.918 [main] ERROR de.com.adsl.sightly.model.Comment - Error Message: null
java.lang.UnsupportedOperationException: null
at org.apache.sling.servlethelpers.MockSlingHttpServletRequest.getParts(MockSlingHttpServletRequest.java:882) ~[org.apache.sling.servlet-helpers-1.1.10.jar:?]
at de.com.adsl.sightly.model.Comment.uploadFile(Feedback.java:137) ~[classes/:?]
at de.com.adsl.sightly.model.Comment.setup(Feedback.java:82) [classes/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_201]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_201]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_201]
at org.apache.sling.models.impl.ModelAdapterFactory.invokePostConstruct(ModelAdapterFactory.java:792) [org.apache.sling.models.impl-1.3.8.jar:?]
at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:607) [org.apache.sling.models.impl-1.3.8.jar:?]
at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:335) [org.apache.sling.models.impl-1.3.8.jar:?]
at org.apache.sling.models.impl.ModelAdapterFactory.getAdapter(ModelAdapterFactory.java:211) [org.apache.sling.models.impl-1.3.8.jar:?]
...
I have looked up various solutions online such as writing two mockito when statements but has not been successful. I would greatly appreciate any form of help or sharing of knowledge if you have encountered the following issue previously. Thank you!
From the source code of MockSlingServletResquest it always throws that exception as it's not supported yet by the mocked class.
https://github.com/apache/sling-org-apache-sling-servlet-helpers/blob/71ef769e5564cf78e49d6679a3270ba8706ae406/src/main/java/org/apache/sling/servlethelpers/MockSlingHttpServletRequest.java#L953
Maybe you should consider writing a servlet, or another approach.
I am using the following Java code in my Spring Boot App (v2.1.6.RELEASE):
#Bean
public IntegrationFlow mailListener() {
SearchTermStrategy searchTermStrategy = (supportedFlags, folder) -> {
SearchTerm search = new AndTerm(new SubjectTerm("trigger the build"), new FlagTerm(new Flags(Flags.Flag.SEEN), false));
return search;
};
return IntegrationFlows.from(Mail.imapInboundAdapter("imaps://usr:pwd#imap.host.com/INBOX").searchTermStrategy(searchTermStrategy).shouldMarkMessagesAsRead(true).get(),
e -> e.poller(Pollers.fixedRate(5000).maxMessagesPerPoll(1)))
.<Message>handle((payload, header) -> logMail(payload))
.get();
}
But this throws an exception com.sun.mail.iap.CommandFailedException: A7 NO Command received in Invalid state. when there is a matching message. This seems to be an issue with the IMAP folder being set as Read-only by default.
There is an onInit protected method in the AbstractMailReceiver class which opens the Folder in Read-Write mode. And I have tried to call the ImapMailReceiver.afterPropertiesSet() which eventually calls the onInit method but that gives me An external 'receiver' ... can't be modified.:
#Bean
private ImapMailReceiver receiver() {
ImapMailReceiver receiver = new ImapMailReceiver("...URL...");
receiver.afterPropertiesSet();
//receiver.setJavaMailProperties(javaMailProperties);
return receiver;
}
Can someone please guide me on how to set the folder in read-write mode?
P.S. In plain Java code I could set the same using inbox.open(Folder.READ_WRITE) and this works.
EDIT 1
If I use the code like this:
#Bean
public IntegrationFlow mailListener() {
SearchTermStrategy searchTermStrategy = (supportedFlags, folder) -> {
SearchTerm search = new AndTerm(new SubjectTerm("trigger the build"),
new FlagTerm(new Flags(Flags.Flag.SEEN), false));
return search;
};
return IntegrationFlows.from(Mail.imapInboundAdapter(receiver()))
.log()
.get();
}
#Bean
public ImapMailReceiver receiver() {
return new ImapMailReceiver(IMAP_URL);
}
I get the following error:
Caused by: java.lang.IllegalArgumentException: No poller has been defined for channel-adapter 'mailListener.org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean#0', and no default poller is available within the context.
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean.initializeAdapter(SourcePollingChannelAdapterFactoryBean.java:186) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean.afterPropertiesSet(SourcePollingChannelAdapterFactoryBean.java:144) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 26 common frames omitted
EDIT 2
If I use the code as below:
#Bean
public IntegrationFlow mailListener() {
SearchTermStrategy searchTermStrategy = (supportedFlags, folder) -> {
SearchTerm search = new AndTerm(new SubjectTerm("trigger the build"),
new FlagTerm(new Flags(Flags.Flag.SEEN), false));
return search;
};
return IntegrationFlows.from(Mail.imapInboundAdapter(receiver()).searchTermStrategy(searchTermStrategy).shouldMarkMessagesAsRead(true).get(),
e -> e.poller(Pollers.fixedRate(5000).maxMessagesPerPoll(1)))
.<Message>handle((payload, header) -> logMail(payload))
.get();
}
#Bean
public ImapMailReceiver receiver() {
return new ImapMailReceiver(IMAP_URL);
}
I get the following error:
Caused by: java.lang.IllegalStateException: An external 'receiver' [imaps://.../INBOX] can't be modified.
at org.springframework.util.Assert.state(Assert.java:94) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.integration.mail.dsl.MailInboundChannelAdapterSpec.assertReceiver(MailInboundChannelAdapterSpec.java:85) ~[spring-integration-mail-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.mail.dsl.ImapMailInboundChannelAdapterSpec.searchTermStrategy(ImapMailInboundChannelAdapterSpec.java:51) ~[spring-integration-mail-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at com.xxx.emailapp.InboundEmailConfig.mailListener(InboundEmailConfig.java:33) ~[classes/:na]
at com.xxx.emailapp.InboundEmailConfig$$EnhancerBySpringCGLIB$$7ab01b59.CGLIB$mailListener$1(<generated>) ~[classes/:na]
at com.xxx.emailapp.InboundEmailConfig$$EnhancerBySpringCGLIB$$7ab01b59$$FastClassBySpringCGLIB$$8ee22ea2.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at com.xxx.emailapp.InboundEmailConfig$$EnhancerBySpringCGLIB$$7ab01b59.mailListener(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 19 common frames omitted
Declaring an explicit receiver bean works
#Bean
public IntegrationFlow mailListener() {
SearchTermStrategy searchTermStrategy = (supportedFlags, folder) -> {
SearchTerm search = new AndTerm(new SubjectTerm("trigger the build"),
new FlagTerm(new Flags(Flags.Flag.SEEN), false));
return search;
};
return IntegrationFlows.from(Mail.imapInboundAdapter(receiver()))
.log()
.get();
}
#Bean
public ImapMailReceiver receiver() {
return new ImapMailReceiver("imaps://usr:pwd#imap.host.com/INBOX");
}
EDIT
You must not call get() on the receiver spec; the framework needs a reference to the spec itself to properly initialize the bean(s).
first I want to say that I have seen all the topics here on stackoverflow for my case but wasn't able to solve my problem anyway.
I need to run scheduled task every night to check weather the task was finished or not - I'm doing like this:
#Service
#Transactional
public class CronBackGroundProcess {
#Autowired
private CronJobService cronJobService;
#Scheduled(cron = "15 01 01 ? * *")
public void StartNightJob() {
CronJobLog log = new CronJobLog();
int count = 0;
try {
log.setStartTime(new Date());
log.setStatus("Entered StartNightJob Function");
cronJobService.saveCronJobLog(log);
List<Task> Tasks = cronJobService.getActive_AND_InArreasTasks();
log.setStatus("Grabbed List of tasks to Check");
cronJobService.saveCronJobLog(log);
for (Task Task : Tasks) {
cronJobService.StartNightJobProcess(Task, true);
count++;
}
} catch (Exception e) {
CronJobLog log2 = new CronJobLog();
log2.setStatus("Error Occurred " + new Date().toString() + e.getMessage());
cronJobService.saveCronJobLog(log2);
}
log.setLoansChecked(count);
log.setStatus("Finished");
log.setEndDate(new Date());
cronJobService.saveCronJobLog(log);
}
}
CronJobService itself is #Transactional and autowires several #Transactional services
#Service
#Transactional
public class CronJobService {
#Autowired
private ProductService productService;
#Autowired
private RepaymentService repaymentService;
#Autowired
private CronJobLogDAO cronJobLogDAO;
#Autowired
private TransferService transferService;
public String StartNightJobProcess(Account account, boolean makeTransfers) {
do something....
}
}
}
the process goes without errors and when all transactions must be committed I receive such error:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:524) ~[spring-orm-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:646) ~[spring-aop-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at ge.shemo.services.core.CronBackGroundProcess$$EnhancerByCGLIB$$30cdcf31.StartNightJob(<generated>) ~[spring-core-4.0.0.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_79]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [na:1.7.0_79]
at java.util.concurrent.FutureTask.run(FutureTask.java:262) [na:1.7.0_79]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) [na:1.7.0_79]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) [na:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_79]
at java.lang.Thread.run(Thread.java:745) [na:1.7.0_79]
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58) ~[hibernate-entitymanager-5.0.1.Final.jar:5.0.1.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515) ~[spring-orm-4.0.0.RELEASE.jar:4.0.0.RELEASE]
... 22 common frames omitted
I can't figure out why.
Also If I launch same function from #Controller it works fine
#Controller
#RequestMapping("/test")
public class test {
#Autowired
private ClientService clientService;
#Autowired
private CronBackGroundProcess cronBackGroundProcess;
#RequestMapping(value = "/test")
#ResponseBody
public void test() throws Exception {
try {
cronBackGroundProcess.StartNightJob();
} catch (Exception e) {
String s = "sd";
}
}
}
So my question is why this function works from controller - commits everything as expected and not works from scheduled task(goes through all process without errors)?
If you can then, put a debug break-point in org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) and then see what the actual exception is.
You not need mark CronBackGroundProcess as #Transactional because in StartNightJob() method you not have access to db all access to DB as I guess you execute in CronJobService.
So remove #Transactional from CronBackGroundProcess and it must help.
I am trying to use dropwizard-sundial and am having trouble with a resource. I'm not sure if it's a classpath issue or if I am failing to register resources properly.
This is my application class' run method:
public void run(DataLoaderApplicationConfiguration configuration, Environment environment) throws Exception {
logger.info("Started DataLoader Application");
final String template = configuration.getTemplate();
environment.healthChecks().register("TemplateHealth", new TemplateHealthCheck(template));
// JOBS
environment.jersey().packages("com.tradier.dataloader.jobs");
}
I get the following error at runtime:
INFO [2015-04-07 15:00:19,737] com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin: Loading annotated jobs from com.tradier.dataloader.jobs.
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.RuntimeException: Unexpected problem: No resource for com/tradier/dataloader/jobs
at org.quartz.classloading.CascadingClassLoadHelper.getJobClasses(CascadingClassLoadHelper.java:217)
at com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin.start(AnnotationJobTriggerPlugin.java:72)
at org.quartz.QuartzScheduler.startPlugins(QuartzScheduler.java:1102)
at org.quartz.QuartzScheduler.start(QuartzScheduler.java:211)
at com.xeiam.sundial.SundialJobScheduler.startScheduler(SundialJobScheduler.java:102)
Check out a working example at https://github.com/timmolter/XDropWizard. It uses annotated jobs. You need to add the package name conatining the annotated jobs in your config.yaml file like this:
sundial:
thread-pool-size: 5
shutdown-on-unload: true
wait-on-shutdown: false
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: org.knowm.xdropwizard.jobs
If you still are getting an exception, leave a report at: https://github.com/timmolter/dropwizard-sundial/issues.
#Jeyashree Narayanan, the jobs package should not be configured in the application class as you have shown, it can be easily done in the yml file. Here is the explanation in simple steps:
Step 1: Configuration in yml file and the Configuration class
sundial:
thread-pool-size: 10
shutdown-on-unload: true
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: com.tradier.dataloader.jobs
tasks: [startjob, stopjob]
Configuration Class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class DropwizardSundialConfiguration extends Configuration {
#Valid
#NotNull
public SundialConfiguration sundialConfiguration = new SundialConfiguration();
#JsonProperty("sundial")
public SundialConfiguration getSundialConfiguration() {
return sundialConfiguration;
}
}
Step 2: Add and configure the dropwizard-sundial bundle in the application class.
public class DropwizardSundialApplication extends Application<DropwizardSundialConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardSundialApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardSundialApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardSundialConfiguration> b) {
b.addBundle(new SundialBundle<DropwizardSundialConfiguration>() {
#Override
public SundialConfiguration getSundialConfiguration(DropwizardSundialConfiguration configuration) {
return configuration.getSundialConfiguration();
}
});
}
}
Step 3: Add the required job classes.
Here is a sample Cron job class:
#CronTrigger(cron = "0 19 13 * * ?")
public class CronJob extends Job {
private static final Logger logger = LoggerFactory.getLogger(CronJob.class);
#Override
public void doRun() throws JobInterruptException {
logger.info("Hello from Cron Job");
}
}
I have also written a blog post and a working application which is available on GitHub with these steps. Please check: http://softwaredevelopercentral.blogspot.com/2019/05/dropwizard-sundial-scheduler-tutorial.html
It appears to be a classpath issue.
From https://github.com/timmolter/Sundial/blob/develop/src/main/java/com/xeiam/sundial/SundialJobScheduler.java#L102:
public static void startScheduler(int threadPoolSize, String annotatedJobsPackageName) {
try {
createScheduler(threadPoolSize, annotatedJobsPackageName);
getScheduler().start(); // ---> Line 102
} catch (SchedulerException e) {
logger.error("COULD NOT START SUNDIAL SCHEDULER!!!", e);
throw new SchedulerStartupException(e);
}
I'm also using Sundial in my dropwizard project, I have all my jobs defined in jobs.xml, Sundial config defined in the .yaml file, and start it as follows:
SundialJobScheduler.startScheduler();
SundialManager sm = new SundialManager(config.getSundialConfiguration(),environment);
environment.lifecycle().manage(sm);