i'm running embedded camunda engine in my application. Now i would like to run second camunda engine with cockpit on different container with the same database. What i did is basically copy-paste of my main applciation configuration only switched dependency from camunda-bpm-spring-boot-starter to camunda-bpm-spring-boot-starter-webapp. I can acess cockpits main page but i'm immediately prompted The process engine you are trying to access does not exist and i don't understand why? On startup i can see that mySpringProcessEngineConfiguration bean is created as well as ProcessEngineFactoryBean bean.
However:
BpmPlatform.getProcessEngineService().getProcessEngineNames();
returns empty set.
Could you please have a look and point my mistake?
main app class:
#SpringBootApplication
public class CamundaCockpitApplication {
public static void main(String[] args) {
SpringApplication.run(CamundaCockpitApplication.class, args);
BpmPlatform.getProcessEngineService().getProcessEngineNames();
}
Camunda confing:
#Configuration
#RequiredArgsConstructor
public class EngineConfiguration {
private final DataSource dataSource;
private final PlatformTransactionManager transactionManager;
private final ResourcePatternResolver resourcePatternResolver;
#Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
SpringProcessEngineConfiguration springConfiguration = new SpringProcessEngineConfiguration();
springConfiguration.setDataSource(dataSource);
springConfiguration.setTransactionManager(transactionManager);
springConfiguration.setDatabaseSchemaUpdate("false");
springConfiguration.setJobExecutorActivate(false);
springConfiguration.setHistory("full");
springConfiguration.setJdbcBatchProcessing(false);
return springConfiguration;
}
#Bean
public ProcessEngineFactoryBean processEngineFactoryBean() {
ProcessEngineFactoryBean engine = new ProcessEngineFactoryBean();
engine.setProcessEngineConfiguration(springProcessEngineConfiguration());
return engine;
}
}
You need to add #EnableProcessApplication annotation to your main class.
see https://docs.camunda.org/manual/7.9/user-guide/spring-boot-integration/process-applications/
Related
I am trying to do a similar thing with my application. I am using following versions of Spring boot and Cassandra:
spring-data-cassandra - 2.0.8.RELEASE
spring-boot-starter-parent - 2.0.4.RELEASE
I need to change some properties(mostly hostnames) of Cassandra on the fly and want it to make a new connection with the application. For config change we have internal Cloud Config Change Management and it runs fine on changes and listens to it.
This is my class :
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
#RefreshScope
#EnableCassandraRepositories(basePackages = {"com.*.*.*.dao.repo"})
public class AppConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AppConfig.class);
#Value("${application['cassandraPort']}")
private String cassandraPort;
#Value("${application['cassandraEndpoint']}")
private String cassandraEndpoint;
#Value("${application['keyspaceName']}")
private String keyspaceName;
#Value("${application['cassandraConsistency']}")
private String cassandraConsistency;
#Value("${application['cassandraUserName']}")
private String cassandraUserName;
#Autowired
private AppConfig appConfig;
public AppConfig() {
System.out.println("AppConfig Constructor");
}
public String getCassandraPort() {
return cassandraPort;
}
public void setCassandraPort(String cassandraPort) {
this.cassandraPort = cassandraPort;
}
public String getCassandraEndpoint() {
return cassandraEndpoint;
}
public void setCassandraEndpoint(String cassandraEndpoint) {
this.cassandraEndpoint = cassandraEndpoint;
}
public String getKeyspaceName() {
return keyspaceName;
}
public void setKeyspaceName(String keyspaceName) {
this.keyspaceName = keyspaceName;
}
public String getCassandraConsistency() {
return cassandraConsistency;
}
public void setCassandraConsistency(String cassandraConsistency) {
this.cassandraConsistency = cassandraConsistency;
}
public String getCassandraUserName() {
return cassandraUserName;
}
public void setCassandraUserName(String cassandraUserName) {
this.cassandraUserName = cassandraUserName;
}
#Bean
// #RefreshScope
public CassandraConverter converter() {
return new MappingCassandraConverter(this.mappingContext());
}
#Bean
// #RefreshScope
public CassandraMappingContext mappingContext() {
return new CassandraMappingContext();
}
#Bean
//#RefreshScope
public CassandraSessionFactoryBean session() {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(this.cluster().getObject());
session.setKeyspaceName(appConfig.getKeyspaceName());
session.setConverter(this.converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Bean
//#RefreshScope
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(appConfig.getCassandraEndpoint());
cluster.setPort(Integer.valueOf(appConfig.getCassandraPort()));
cluster.setUsername(appConfig.getCassandraUserName());
cluster.setPassword("password");
cluster.setQueryOptions(new QueryOptions().setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM));
return cluster;
}
}
However, when I try to use #RefreshScope with that Configuration class, the application fails to start. This is what it shows in console :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 2 of constructor in org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration required a bean of type 'com.datastax.driver.core.Cluster' that could not be found.
- Bean method 'cassandraCluster' not loaded because auto-configuration 'CassandraAutoConfiguration' was excluded
Action:
Consider revisiting the entries above or defining a bean of type 'com.datastax.driver.core.Cluster' in your configuration.
Is there some guidelines on using #RefreshScope with Cassandra Bean? If anyone has done that earlier can you share the same?
You're mixing a couple of things here.
The config carries properties and bean definitions.
#RefreshScope on AppConfig causes some interference with Spring Boot's auto-configuration and the declared beans aren't used (that's why you see Parameter 2 of constructor…).
To clean up, we will reuse what Spring Boot provides as much as possible, and only declare what's really needed.
Follow these steps to solve the issue (based on your code above):
Create a #ConfigurationProperties bean that encapsulates your properties, or better, reuse CassandraProperties.
Re-enable CassandraAutoConfiguration and remove your own MappingContext and CassandraConverter beans, keep only Cluster and Session bean definitions
Declare Cluster and Session beans as needed and make them use #RefreshScope. Your #Configuration class should look like:
Example Configuration:
#Configuration
public class MyConfig {
#Bean(destroyMethod = "close")
#RefreshScope
public Cluster cassandraCluster(CassandraProperties properties) {
Cluster.Builder builder = Cluster.builder().addContactPoints(properties.getContactPoints().toArray(new String[0]))
.withoutJMXReporting();
return builder.build();
}
#Bean(destroyMethod = "close")
#RefreshScope
public Session cassandraSession(CassandraProperties properties, Cluster cluster) {
return cluster.connect(properties.getKeyspaceName());
}
}
I have multiple library classes in my project which need to be injected into a service class. This is the error statement for IntegrationFactory class:
Consider defining a bean of type 'com.ignitionone.service.programmanager.integration.IntegrationFactory' in your configuration.
This error is coming on almost every injection where this library class is injected.
I have already added the Library package in #ComponentScan, but, as it is read-only file, I can not annotate the library class. I came to know from some answer here that Spring can not inject classes which it does not manage. This library is not built on spring.
I have tried to create a #Bean method which returns the IntegrationFactory(class in question) in the class where #Inject is used, but this too does not seem to work.
How can this be done, preferably without creating a stub/copy class?
This is EngagementServiceImpl class snippet:
#Inject
public EngagementServiceImpl(EngagementRepository engagementRepository,
#Lazy IntegrationFactory integrationFactory, TokenRepository tokenRepository,
EngagementPartnerRepository engagementPartnerRepository, MetricsService metricsService) {
this.engagementRepository = engagementRepository;
this.integrationFactory = integrationFactory;
this.tokenRepository = tokenRepository;
this.engagementPartnerRepository = engagementPartnerRepository;
this.metricsService = metricsService;
}
This is injection part:
#Autowired
private EngagementService engagementService;
This is ConfigClass:
#Configuration
public class ConfigClass {
#Bean
public IntegrationFactory getIntegrationFactory(){
Map<String, Object> globalConfig = new HashMap<>();
return new IntegrationFactory(globalConfig);
}
#Bean
#Primary
public EntityDataStore getEntityDataStore(){
EntityModel entityModel = Models.ENTITY;
return new EntityDataStore(this.dataSource(), entityModel );
}
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.build();
}
}
You need to add your bean definitions in a configuration class.
#Configuration
public class ServiceConfig {
#Bean
public IntegrationFactory getIntegrationFactory(){
// return an IntegrationFactory instance
}
}
Then you have to make sure your #Configuration class gets detected by Spring, either by having it within your scanned path or by manually importing it via #Import from somewhere withing you scanned path. An example of #Import, considering you are using Spring Boot.
#Import(ServiceConfig.class)
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Hope this helps!
Your Bean IntegrationFactory can't be found, as it is not annotated with any Spring stereotype and therefore not recognized by the component scan.
As you have multiple options to provide an instance of your class to the application context, read the Spring documentation (which also includes samples) to find out which one fits you the most:
https://docs.spring.io/spring/docs/5.1.0.RELEASE/spring-framework-reference/core.html#beans-java-basic-concepts
One Option would be to create a factory which provides an instance of your class to the application context, like it is stated in the documentation:
#Configuration
public class AppConfig {
#Bean
public IntegrationFactory myIntegrationFactory() {
return new IntegrationFactory();
}
}
Do not forget to add the Configuration to the application context.
I work on Spring Boot project. I added localresolver method into a bean. And I want to execute this bean after running the app because I will call a webservice to get the default value of the language.
How can I force this bean to be called after running the app ?
Some people said that we can do it by calling #postconstruct.
this is my code :
#SpringBootApplication
public class Config extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Config.class, args);
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(LanguageController.getLanguage().get(0));
return slr;
}
}
the method LanguageController.getLanguage() should not be called before running the app, that's why I would like to execute the bean after.
Summary: Adding the #ComponentScan (or #SpringBootApplication) annotation to my application class changes the behaviour of SpringApplicationBuilder.properties() and breaks my integration test.
I am using a cut-down version of the Spring Boot sample:
spring-boot-sample-websocket-jetty
I have removed everything except what is required for the "echo" example (and I'm using Spring Boot 1.3.3).
I am left with the following SampleJettyWebSocketsApplication code:
#Configuration
#EnableAutoConfiguration
//#ComponentScan // --- If I uncomment this the test breaks ---
#EnableWebSocket
public class SampleJettyWebSocketsApplication
implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
}
#Bean
public EchoService echoService() {
return new DefaultEchoService("Did you say \"%s\"?");
}
#Bean
public WebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler(echoService());
}
public static void main(String[] args) {
SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
}
}
And the following test class (code straight from the Spring Boot samples):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(SampleJettyWebSocketsApplication.class)
#WebIntegrationTest({"server.port=0"})
#DirtiesContext
public class SampleWebSocketsApplicationTests {
private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);
#Value("${local.server.port}")
private int port = 1234;
#Test
public void echoEndpoint() throws Exception {
logger.info("Running the echoEndpoint test. Port: " + port + ". Path: /echo/websocket");
ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + this.port
+ "/echo/websocket")
.run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close();
assertThat(count).isEqualTo(0);
assertThat(messagePayloadReference.get())
.isEqualTo("Did you say \"Hello world!\"?");
}
#Configuration
static class ClientConfiguration implements CommandLineRunner {
#Value("${websocket.uri}")
private String webSocketUri;
private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference<String> messagePayload = new AtomicReference<String>();
#Override
public void run(String... args) throws Exception {
logger.info("Waiting for response: latch=" + this.latch.getCount());
if (this.latch.await(10, TimeUnit.SECONDS)) {
logger.info("Got response: " + this.messagePayload.get());
}
else {
logger.info("Response not received: latch=" + this.latch.getCount());
}
}
#Bean
public WebSocketConnectionManager wsConnectionManager() {
logger.info("Setting up SimpleClientWebSocketHandler...");
WebSocketConnectionManager manager = new WebSocketConnectionManager(client(),
handler(), this.webSocketUri);
manager.setAutoStartup(true);
return manager;
}
#Bean
public StandardWebSocketClient client() {
return new StandardWebSocketClient();
}
#Bean
public SimpleClientWebSocketHandler handler() {
logger.info("Creating new SimpleClientWebSocketHandler using SimpleGreetingService...");
return new SimpleClientWebSocketHandler(greetingService(), this.latch,
this.messagePayload);
}
#Bean
public GreetingService greetingService() {
return new SimpleGreetingService();
}
}
}
Running the Application and the unit test as above all is fine but if I uncomment the #ComponentScan annotation on the application class the application still runs OK but the test breaks with the error:
Could not resolve placeholder 'websocket.uri' in string value "${websocket.uri}".
I have read at setting-the-run-time-properties-on-springapplicationbuilder that:
The properties you configure on SpringApplicationBuilder are made available in your application's Environment, not as system properties.
And in the #ComponentScan javadoc that:
If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
But I don't understand why the behaviour changes when the #ComponentScan annotation is added.
How can I set the System Property websocket.uri in the test when the application class is annotated with #ComponentScan (or #SpringBootApplication)?
(I aim to use #SpringBootApplication, which incorporates #ComponentScan, but I can't until I get this working.)
There are several ways to add a system properties.
Solution 1:
Add arguments for Test in format of -Dabc=xyz, that will add property abc to system properties.
Solution 2:
Just like floor 0.
Solution 3:
Just let spring-boot load the properties, such as classpath:bootstrap.yml, and you can specify whatever properties in there.
The annotation #ComponentScan will enable auto scanning based on current package or ComponentScan#basePackages. Which means SampleWebSocketsApplicationTests.ClientConfiguration will be scanned cause they have same base package samples.websocket.jetty.
However, SampleWebSocketsApplicationTests.ClientConfiguration should not be parsed by SpringJUnit4ClassRunner cause we need parse it in SampleWebSocketsApplicationTests#echoEndpoint manually. It's should only be parsed by ApplicationContext created in echoEndpoint().
What's more, #SpringBootApplication equals to use #Configuration and #EnableAutoConfiguration and #ComponentScan together, so comment out #ComponentScan or #SpringBootApplication will have same effect.
My suggestion is move class SampleWebSocketsApplicationTests into package samples.websocket.jettytest(different from samples.websocket.jetty) and enable #ComponentScan or #SpringBootApplication on SampleJettyWebSocketsApplication and try again. It should work.
Adding my thoughts on this (from whatever i could gather from your code):
-Try adding the property websocket.uri in you application properties or if your project contains src/test/resources/test.properties then add it into your test.properties file.#ComponentScan should pick it up.
-Else,you could just say :
public static void main(String[] args) {
System.setProperty("websocket.uri","<your uri>");
SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
}
Hope it helps.
I have small test project to test Spring annotations:
where in nejake.properties is:
klucik = hodnoticka
and in App.java is:
#Configuration
#PropertySource("classpath:/com/ektyn/springProperties/nejake.properties")
public class App
{
#Value("${klucik}")
private String klc;
public static void main(String[] args)
{
AnnotationConfigApplicationContext ctx1 = new AnnotationConfigApplicationContext();
ctx1.register(App.class);
ctx1.refresh();
//
App app = new App();
app.printIt();
}
private void printIt()
{
System.out.println(klc);
}
}
It should print hodnoticka on console, but prints null - String value is not initialized. My code is bad - at the moment I have no experience with annotation driven Spring. What's bad with code above?
You created the object yourself
App app = new App();
app.printIt();
how is Spring supposed to manage the instance and inject the value?
You will however need
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
to make the properties available. Also, because the App bean initialized for handling #Configuration is initialized before the resolver for #Value, the value field will not have been set. Instead, declare a different App bean and retrieve it
#Bean
public App appBean() {
return new App();
}
...
App app = (App) ctx1.getBean("appBean");
You need to access the property from a Spring bean, and you need to properly wire in the properties. First, add to your config class this:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
props.setLocations(new Resource[] { new ClassPathResource("com/ektyn/springProperties/nejake.properties") }); //I think that's its absolute location, but you may need to play around with it to make sure
return props;
}
Then you need to access them from within a Spring Bean. Typically, your config file should not be a bean, so I would recommend you make a separate class, something like this:
#Component //this makes it a spring bean
public class PropertiesAccessor {
#Value("${klucik}")
private String klc;
public void printIt() {
System.out.println(klc);
}
}
Finally, add this to your config to make it find the PropertiesAccessor:
#ComponentScan("com.ektyn.springProperties")
Then you can access the PropertiesAccessor bean from your app context and call its printIt method.