I took help from this forum : https://community.alfresco.com/thread/225090-spring-boot-activiti-5180-and-drools-integration-issue. I was able to Autowire the ProcessEngine, get the process engine configuration and then while adding the deployer I got struck. The snippet of code is :
SpringProcessEngineConfiguration sp = (SpringProcessEngineConfiguration)
processEngine.getProcessEngineConfiguration();
List<Deployer> listDeployer = new ArrayList<Deployer>();
listDeployer.add(new RulesDeployer());
sp.setCustomPostDeployers(listDeployer); // <--setCustomPostDeployers function is not called
How can I achieve this and call the setCustomPostDeployers function to integrate Drools with Activiti. Can any one please help me on this issue?
It takes me time to figure it out, but after reading some interesting posts and some documentation I have finally created an example using Activiti, Spring-Boot and Drools.
In your case, you are modifying the existing SpringBootConfiguration before using the processEngine, but according to my tests, is too late to adding the custom deployers there, due to the resources has been already read. Then you must set the configuration much earlier.
The documentation in general is pointing out to change the 'activiti.cfg.xml' but this is for spring and useless for spring-boot. Then the idea is to generate a configuration class as Spring Boot use to do.
#Configuration
public class ProcessEngineConfigDrlEnabled {
#Autowired
private DataSource dataSource;
#Autowired
private PlatformTransactionManager transactionManager;
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
try {
config.setDeploymentResources(getBpmnFiles());
} catch (IOException e) {
e.printStackTrace();
}
config.setDataSource(dataSource);
config.setTransactionManager(transactionManager);
//Here the rulesdeployer is added.
config.setCustomPostDeployers(Arrays.asList(new RulesDeployer()));
return config;
}
/*Read folder with BPMN files and folder with DLR files */
private Resource[] getBpmnFiles() throws IOException {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] bpmnResources = resourcePatternResolver.getResources("classpath*:" + BPMN_PATH + "**/*.bpmn20.xml");
Resource[] drlResources = resourcePatternResolver.getResources("classpath*:" + DRL_PATH + "**/*.drl");
return (Resource[]) ArrayUtils.addAll(bpmnResources, drlResources);
}
#Bean
public ProcessEngineFactoryBean processEngine() {
ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
factoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return factoryBean;
}
...
}
As usual, this class must be in a packet that Spring boot can read (in the packet hierarchy of the main class).
At this example, I have #Autowired the datasource and the transactionManager to use the original one in from the default configuration. If not, you must implement yours and add them to the configuration.
Related
I have a use case where by the #CachePut annotation adds an entry to the cache, and I have to retrieve it manually (via code).
I can see that the total backup count gives me 1 as the number of entries, but all the maps give me the size as 0. So, I am not sure what I am doing wrong.
Here's my code
HazelcastConfig.java
#Configuration
public class HazelcastConfig {
#Bean
public Config hazelcastConf() {
Config c = new Config()
.setInstanceName("hazelcast-instance")
.addMapConfig(
new MapConfig()
.setName("testmap")
.setEvictionConfig(
new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setMaxSizePolicy(MaxSizePolicy.PER_NODE)
.setSize(1000)
)
.setTimeToLiveSeconds(500000)
);
c.getNetworkConfig().getRestApiConfig().setEnabled(true);
c.getNetworkConfig().getRestApiConfig().enableGroups(RestEndpointGroup.DATA);
return c;
}
}
TestServiceImpl.java
#Service
public class TestServiceImpl implements TestService {
#Autowired
#Lazy
private RestTemplate restTemplate;
#Override
#CachePut(value = "testmap", key="1")
public String getId() {
System.out.println("--------------------------");
System.out.println("-------INSIDE getId-------");
String id = null;
CBObject obj = restTemplate.getForObject("http://localhost:3000/testCB", CBObject.class);
if (null != obj && null != obj.getId()) {
id = String.valueOf(obj.getId());
}
System.out.println("-------- EXIT getId-------");
System.out.println("--------------------------");
return id;
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
TestController.java
#RestController
#RequestMapping("/v1")
public class TestController {
#Autowired
private TestService testService;
#GetMapping("/testCB")
public ResponseEntity<?> doCB() {
Map<String, String> resp = new HashMap<>();
String id = testService.getId();
if (null != id) {
resp.put("id", id);
}
Config config = new HazelcastConfig().hazelcastConf();
System.out.println(config.getMapConfig("testmap").getTotalBackupCount()); // 1
HazelcastInstance hz = Hazelcast.getHazelcastInstanceByName(config.getInstanceName());
System.out.println(hz.getReplicatedMap("testmap").size()); // 0
System.out.println(hz.getMap("testmap").size()); // 0
System.out.println(hz.getMultiMap("testmap").size()); // 0
return ResponseEntity.status(HttpStatus.ACCEPTED).body(resp);
}
}
Have you explicitly enabled caching using the #EnableCaching annotation (Ref Doc, Javadoc)?
Also, see the guidance from Spring Boot in the Ref Doc on Caching if you are using Spring Boot.
Furthermore, when using Spring Boot, you can either add the command-line switch --debug to your launch command or set the debug property to true in Spring Boot application.properties to get output from the Auto-configuration that has been applied. In particular you will want to see that the CacheAutoConfiguration class has been processed.
If you are NOT using Spring Boot, then in addition to the #EnableCaching annotation, you will also need to explicitly declare a CacheManager bean, such as:
#Bean
HazelcastCacheManager cacheManager(HazelcastInstance hazelcaseInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
This will require the com.hazelcast:hazelcast-spring JAR dependency on your runtime classpath.
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>${hazelcast.version}</version>
<scope>runtime</scope>
</dependency>
For example.
NOTE: Do you not confuse the HazelcastCacheManager Spring Cache Abstraction CacheManager implementation, which requires the hazelcast-spring JAR and is required by Spring's Cache Abstraction either with or without Spring Boot, with Hazelcast's standard HazelcastCacheManager. These 2 classes are not the same thing.
Alternatively, you could also use Hazelcast as a JCache caching provider implementation in either Spring Framework or Spring Boot. The core Spring Framework offers support for using JCache as well. When using Spring Boot, you will need to specify the JCache cache provider type for Hazelcast (i.e. Embedded or Client/Server). I will leave this as an exercise for you to figure out.
Lastly, I recently built an example for my own testing purposes using Hazelcast as a caching provider in Spring Framework's Cache Abstraction using Spring Boot, if you would like to take a look.
Hope this helps!
Cheers!
I am trying to use the RepliyngKafkaTemplate like I managed to use the KafkaTemplate in a REST controller.
#RestController
public class TestController {
#Autowired
private ReplyingKafkaTemplate<Object, KafkaExampleRecord, KafkaExampleRecord> replyingTemplate;
#PostMapping("/test/request")
public void requestReply(#RequestBody KafkaExampleRecord record) throws ExecutionException, InterruptedException, TimeoutException {
ProducerRecord<Object, KafkaExampleRecord> producerRecord = new ProducerRecord<>("mytopic", record);
RequestReplyFuture<Object, KafkaExampleRecord, KafkaExampleRecord> replyFuture = replyingTemplate.sendAndReceive(producerRecord);
SendResult<Object, KafkaExampleRecord> sendResult = replyFuture.getSendFuture().get(10, TimeUnit.SECONDS);
ConsumerRecord<Object, KafkaExampleRecord> consumerRecord = replyFuture.get(10, TimeUnit.SECONDS);
}
}
However I am getting the following exception.
Field replyingTemplate in com.blah.KafkaController required a bean of type 'org.springframework.kafka.requestreply.ReplyingKafkaTemplate' that could not be found.
I enabled auto configuration like this.
#Configuration
#EnableKafka
public class KafkaConfig {
}
All Kafka settings are in my application.yml.
What else do I need? Do I really have to define beans? Seems unnecessary.
Do I really have to define beans? Seems unnecessary.
Yes, you have to declare a beans for the replying template (including the reply container); Spring Boot only auto configures a simple KafkaTemplate.
Can you check, whether you are scanning the basePackages correctly. Sometimes, you may end-up with this issue, if you not scanning the packages correctly, and I have experienced this many times in the Spring Boot application.
#ComponentScan(
basePackages = {
"x.x.x.x"
}
)
I am running Restful web-service as standalone application using Jersey.
Below are my service classes which serve's the requests.
LoginServiceImpl.java
#Component
public class LoginServiceImpl implements LoginService {
#Value("${login.service.defaultmessage}")
private String defaultMessage;
#Autowired
private EmLoginDAO emLoginDAO;
#Override
public String defaultCall() {
return defaultMessage;
}
#Override
public String updatePassword(List<Login> userList) {
System.out.println(emLoginDAO + "\n" + userList);
emLoginDAO.save(userList);
return "Passwords Updated...";
}
#Override
public List<Login> getPasswords() {
System.out.println("OBJECT: " + emLoginDAO);
List<Login> userList = null;
userList = emLoginDAO.findAll();
return userList;
}
}
LoginService.java
#Component
#Path("/user")
public interface LoginService {
#GET
public String defaultCall();
#POST
#Path(value = "/print")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
public String updatePassword(List<Login> userList);
#GET
#Path(value = "/getpassword")
#Produces(MediaType.APPLICATION_XML)
public List<Login> getPasswords();
}
Below is my spring configuration file.
<context:component-scan base-package="com.em.login" />
<context:annotation-config />
After starting the service when I call the respective method get called.
But my defaultMessage and emLoginDAO objects are null. So it is not referring to the properties and spring configuration files.
So can any one please help me to get this correct. Or to find a way to set the properties and spring configuration file paths to Jersey.
Update
Closeable server = null;
try {
DefaultResourceConfig resourceConfig = new DefaultResourceConfig(
LoginServiceImpl.class);
resourceConfig.getContainerResponseFilters().add(
new GZIPContentEncodingFilter());
server = SimpleServerFactory.create(serviceurl,
resourceConfig);
System.in.read();
} catch (IOException e) {
} finally {
if (server != null) {
try {
server.close();
} catch (IOException ex) {
}
}
}
I think this is the culprit:
DefaultResourceConfig resourceConfig = new DefaultResourceConfig(LoginServiceImpl.class);
You are using Spring's IOC to create the objects and do the autowiring, but you are not getting the instance from the Spring container. You need to get the LoginServiceImpl instance from the Spring container, and not have Jersey create it (Jersey does not know how to autowire your #Autowired properties.
You should use the Spring integration with Jersey, seen here.
Edit to respond to your comment, you posted this code:
LoginServiceImpl loginServiceImpl = (LoginServiceImpl) SpringApplicationContext.getBean("loginServiceImpl");
DefaultResourceConfig resourceConfig = new DefaultResourceConfig( loginServiceImpl.getClass());
You are creating a loginServiceImpl via the spring container, and I'll bet if you check your autowired fields will be there.
However, the second line where you use loginServiceImpl.getClass() this is going to create a new LoginServiceImpl, which is not the same one as the loginServiceImpl you got from the context, so you still are going to have the same problem.
You could take a look at Microserver, that will do all the wiring between Jersey and Spring for you (and setup a Grizzly webserver). From the tags I notice you are using Spring boot, with Microserver: micro-boot module you can do (in a class in package com.em.login):
public static void main(String[] args){
new MicrobootApp(()->"test-app").run();
}
And it should wire up Grizzly, Jersey & Spring with Spring-boot enabled for any backend (non-Jax-rs) dependencies.
Alternatively without Spring Boot (plain old Jersey and Spring)
public static void main(String[] args){
new MicroserverApp(()->"test-app").run();
}
To do it manually, you will need to add the Jersey-Spring integration jar to your classpath and make sure both are configured in a way that interoperates (i.e. I think a registering Spring ContextListener is essential). There is an example app here.
Have you configured those two in your spring configuration files?
I mean have you annotated EmLoginDAO also as stereotype Component?
I got this working.
Referred the this part of the Jersey documentation.
Below is the code I have used to make this working.
ResourceConfig resourceConfig = new ResourceConfig(LoginServiceImpl.class);
resourceConfig.register(org.glassfish.jersey.server.filter.UriConnegFilter.class);
resourceConfig.register(org.glassfish.jersey.server.spring.SpringComponentProvider.class);
resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
resourceConfig.property("contextConfigLocation", "classpath:/spring-config.xml");
URI serviceUri = UriBuilder.fromUri(serviceHost).port(servicePort).build();
server = SimpleContainerFactory.create(serviceUri, resourceConfig);
Thank you all for helping.
This is related to
MongoDB and SpEL Expressions in #Document annotations
This is the way I am creating my mongo template
#Bean
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
String dbname = getCustid();
return new SimpleMongoDbFactory(new MongoClient("localhost"), "mydb");
}
#Bean
MongoTemplate mongoTemplate() throws UnknownHostException {
MappingMongoConverter converter =
new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext());
return new MongoTemplate(mongoDbFactory(), converter);
}
I have a tenant provider class
#Component("tenantProvider")
public class TenantProvider {
public String getTenantId() {
--custome Thread local logic for getting a name
}
}
And my domain class
#Document(collection = "#{#tenantProvider.getTenantId()}_device")
public class Device {
-- my fields here
}
As you see I have created my mongotemplate as specified in the post, but I still get the below error
Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1057E:(pos 1): No bean resolver registered in the context to resolve access to bean 'tenantProvider'
What am I doing wrong?
Finally figured out why i was getting this issue.
When using Servlet 3 initialization make sure that you add the application context to the mongo context as follows
#Autowired
private ApplicationContext appContext;
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
return new SimpleMongoDbFactory(new MongoClient("localhost"), "apollo-mongodb");
}
#Bean
MongoTemplate mongoTemplate() throws UnknownHostException {
final MongoDbFactory factory = mongoDbFactory();
final MongoMappingContext mongoMappingContext = new MongoMappingContext();
mongoMappingContext.setApplicationContext(appContext);
// Learned from web, prevents Spring from including the _class attribute
final MappingMongoConverter converter = new MappingMongoConverter(factory, mongoMappingContext);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
return new MongoTemplate(factory, converter);
}
Check the autowiring of the context and also
mongoMappingContext.setApplicationContext(appContext);
With these two lines i was able to get the component wired correctly to use it in multi tenant mode
The above answers just worked partially in my case.
I've been struggling with the same problem and finally realized that under some runtime execution path (when RepositoryFactorySupport relies on AbstractMongoQuery to query MongoDB, instead of SimpleMongoRepository which as far as I know is used in "out of the box" methods provided by SpringData) the metadata object of type MongoEntityMetadata that belongs to MongoQueryMethod used in AbstractMongoQuery is updated only once, in a method named getEntityInformation()
Because MongoQueryMethod object that holds a reference to this 'stateful' bean seems to be pooled/cached by infrastructure code #Document annotations with Spel not always work.
As far as I know as a developer we just have one choice, use MongoOperations directly from your #Repository bean in order to be able to specify the right collection name evaluated at runtime with Spel.
I've tried to use AOP in order to modify this behaviour, by setting a null collection name in MongoEntityMetadata but this does not help because changes in AbstractMongoQuery inner classes, that implement Execution interface, would also need to be done in order to check if MongoEntityMetadata collection name is null and therefore use a different MongoTemplate method signature.
MongoTemplate is smart enough to guess the right collection name by using its private method
private <T> String determineEntityCollectionName(T obj)
I've a created a ticket in spring's jira https://jira.spring.io/browse/DATAMONGO-1043
If you have the mongoTemplate configured as in the related issue, the only thing i can think of is this:
<context:component-scan base-package="com.tenantprovider.package" />
Or if you want to use annotations:
#ComponentScan(basePackages = "com.tenantprovider.package")
You might not be scanning the tenant provider package.
Ex:
#ComponentScan(basePackages = "com.tenantprovider.package")
#Document(collection = "#{#tenantProvider.getTenantId()}_device")
public class Device {
-- my fields here
}
I'm using CXF with Spring to publish and to consume my WebServices in JBoss 5.1. All works fine.
However, there's a thing that's I think very tedious: to put a jaxws:endpoint tag for every WebService in applicationContext.xml.
There's realy no way to do that with annotations? Thanks for all.
As time pass, there arise some new possibilities.
Working with CXF/SpringBoot (SpringBoot: 1.2.3, CXF: 3.10, Spring: 4.1.6) there is a nice alternative in order to get rid of the jaxws:endpoint configuration in cxf-servlet.xml, as jonashackt pointed out in nabble.com. However, this solution is only possible if there is only one endpoint in the application (at least I did not succeed to configure more than one).
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean dispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/api/*");
}
#Bean(name="cxf")
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public MyServicePortType myService() {
return new MyServiceImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), myService());
endpoint.publish("/MyService");
return endpoint;
}
Where MyServicePortType is a class having the #WebService annotation. This Endpoint is then called for URL's like "localhost:8080/api/MyService"
Of course these #Bean declarations may be placed in any other spring config class.
In contrary to the copied original solution I suggest to instantiate the Bus (cxf-Bean) by using the factory method instead of the direct "new SpringBus()":
BusFactory.newInstance().createBus()
There are some annotations to configure things that you can also put in <jaxws:endpoint>. An annotation to declare a CXF endpoint would be nice.
You can configure an endpoint using code instead of Spring XML. This can be handy if you have a lot of repetitive configuration that you can factor out. Or if you have certain endpoints configured differently in different environments.
For example:
#Autowired var authImpl: Auth = _
#Autowired var faultListener: FaultListener = _
def initWebServices() {
var sf: JaxWsServerFactoryBean = _
val propMap = mutable.HashMap[String, AnyRef]("org.apache.cxf.logging.FaultListener"->faultListener.asInstanceOf[AnyRef])
sf = new JaxWsServerFactoryBean
sf.setServiceBean(authImpl)
sf.setAddress("/auth")
sf.setServiceName(new QName("http://auth.ws.foo.com/", "auth", "AuthService"))
sf.setProperties(propMap)
sf.create
// more services...