Drools - Resource does not have neither a source nor a target path - spring-boot

I'm very new to Drools and adapted the Spring Boot configuration i found here to enable me to read a bunch of rules from a Database as String's instead of reading them from DRL Files in an application folder.
When I startup my application using the code below i'm get the following exception using Spring Boot, Drools 6.5.Final, Java 1.8:
Caused by: java.lang.RuntimeException: Resource does not have neither
a source nor a target path. Impossible to add it to the bundle. Please
set either the source or target name of the resource before adding
it.null at
org.drools.compiler.kie.builder.impl.KieFileSystemImpl.write(KieFileSystemImpl.java:95)
~[drools-compiler-6.5.0.Final.jar:6.5.0.Final]
package com.example.demo;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DroolsDatabaseConfig {
#Autowired
private DataService dataService;
#Bean
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource resource : getRulesFromDB()) {
kieFileSystem.write(resource);
}
return kieFileSystem;
}
private List<Resource> getRulesFromDB() throws IOException {
List<Resource> resources = new ArrayList<Resource>();
List<String> rules = dataService.getRulesFromDB();
for (String rule : rules){
Resource r = ResourceFactory.newInputStreamResource(new ByteArrayInputStream(rule.getBytes(StandardCharsets.UTF_8)));
resources.add(r);
}
return resources;
}
#Bean
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();
kieRepository.addKieModule(new KieModule() {
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
}
private KieServices getKieServices() {
return KieServices.Factory.get();
}
#Bean
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
#Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession();
}
}
Any ideas on how i can resolve this to allow me load the Rules from a String?
Thanks in advance

Related

Property or field 'config' cannot be found on null

I am trying to Implement InitializingBean on my StudentDaoImpl.class and in the function afterPropertiesSet() trying to execute an expression using ExpressionParser, but Spring is saying that property config is not found or is null even if I have declared config in my App.propperties.
AppConfig.class(Configuration Class)
package config;
import dao.StudentDAOImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
#Configuration
#PropertySource("classpath:App.properties")
public class AppConfig {
#Bean("jdbcTemplete")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource());
return template;
}
#Bean("dataSource")
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/springjdbc");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
#Bean("StudentDaoImpl")
public StudentDAOImpl studentDaoImpl() {
StudentDAOImpl studentdao = new StudentDAOImpl(jdbcTemplate());
return studentdao;
}
#Bean("getProperties")
public static PropertySourcesPlaceholderConfigurer getProperties() {
return new PropertySourcesPlaceholderConfigurer();
}
}
StudentDaoImpl.class (I have removed methods of StudentDao Interface from the code below for you to see properly)
package dao;
import entities.Student;
import mapper.StudentMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
public class StudentDAOImpl implements StudentDAO, InitializingBean {
private JdbcTemplate jdbcTemplate;
#Value("${config}")
String s;
public StudentDAOImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public void afterPropertiesSet() throws Exception {
ExpressionParser expressionParser = new SpelExpressionParser();
System.out.println(s);
Expression expression = expressionParser.parseExpression("config");
String result = (String)expression.getValue();
System.out.println(result);
// if(result == true) {
// System.out.println("[Configuration]: Java Configuration");
// } else {
// System.out.println("[Configuration]: XML Configuration");
// }
}
}
App.properties
config=java
Also, I have seen that I was able to access the property from App.properties using #Value but not with ExpressionParser, Please help me solve this issue.
?
You are parsing a String, not the field; use
Expression expression = expressionParser.parseExpression(config);
to parse an expression in field config - however the value java is not a valid expression.
It is not at all clear what you are trying to do.

Getting 500 error while testing webflux code with wiremock

I have spring webflux app with user controller class with end point "/user and user service class.The user service class making call to external api. I am trying to test the service class using wiremock and junit 5 to mock out external api.. However I am getting below error ->
021-07-30 18:22:52.511 ERROR 16974 --- [o-auto-1-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.net.ConnectException: Connection refused
Code is uploaded at path : https://github.com/neeleshsethi/wiremockdemp/tree/master
It seems it cannot find controller as adding a print statement in controller is not printing anything. Below is the code ->
#Service
public class UserService {
#Autowired
WebClient webClient;
public Mono<User> createuser(User user) {
return webClient.post()
.uri("/usercreate")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(user))
.retrieve()
.bodyToMono(User.class);
}
}
WireMock Inti class:
package com.example.wiremockdemo;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import java.util.Map;
public class WireMockInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
WireMockServer wireMockServer = new WireMockServer(new WireMockConfiguration().dynamicPort());
wireMockServer.start();
applicationContext.addApplicationListener( applicationEvent ->
{
if(applicationEvent instanceof ContextClosedEvent)
{
wireMockServer.stop();
}
}
);
applicationContext.getBeanFactory().registerSingleton("wireMockServer", wireMockServer);
TestPropertyValues.of("externalBaseUrl",wireMockServer.baseUrl())
.applyTo(applicationContext);
}
}
test class:
package com.example.wiremockdemo;
import com.example.wiremockdemo.model.User;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import reactor.core.publisher.Mono;
import java.awt.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(initializers = {WireMockInit.class})
class WiremockdemoApplicationTests {
#Autowired
WebTestClient webTestClient;
#Autowired
private WireMockServer wireMockServer;
#LocalServerPort
private Integer port;
#Test
void createUsertest() {
System.out.println("Creating stub");
wireMockServer.stubFor(
WireMock.post("/usercreate")
.willReturn(
aResponse()
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBodyFile("response.json"))
);
byte[] temp = webTestClient.post()
.uri("http://localhost:" + port + "/user")
// .uri("/user")
.contentType(MediaType.APPLICATION_JSON)
//.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(createUser()))
.exchange()
.expectBody()
.returnResult()
.getResponseBody();
String s = new String(temp);
System.out.println("Response :" +s);
}
public User createUser()
{
return User.builder()
.firstName("neel")
.age(32)
.id(1234)
.build();
}
}
Change this line to:
TestPropertyValues.of(Map.of("externalBaseUrl", wireMockServer.baseUrl()));
This is the correct way to set test properties.

Reactive way of reading YAML with Jackson using Spring boot webflux

The yamlObjectMapper in configuration
#Bean
public ObjectMapper yamlObjectMapper() {
ObjectMapper yamlObjectMapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature
.WRITE_DOC_START_MARKER));
yamlObjectMapper.findAndRegisterModules();
return yamlObjectMapper;
}
The Service to parse yaml file
#Service
public class CustomerService {
#Autowired
#Qualifier("yamlObjectMapper")
private ObjectMapper yamlObjectMapper;
public Customer get() {
try {
InputStream inputStream = ResourceUtils.getURL("classpath:/files/test.yaml").openStream();
return yamlObjectMapper.readValue(inputStream, Customer.class);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
#Data
public static class Customer {
private String name;
private String surname;
private String email;
}
}
I guess IO operations are blocking, how this can be done using reactive way?
I would rather use configuration binding since probably you need to read it once.
package com.vob.webflux.webfilter.controller;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#PropertySource(value = "classpath:config.yml", factory= YamlPropertySourceFactory.class)
#Getter
public class YamlFooProperties {
#Value("${test}")
private String test;
}
Factory
package com.vob.webflux.webfilter.controller;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Properties;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
Source factory from

How to create same Bean using Constructor Injection in Spring boot for different property values

I have to create multiple beans of same type for different property value which is to be injected using constructor.
Currently I have used Bean scope as Prototype & created multiple methods to read different properties to create new object. How to combine all the different methods into single method and to supply different values at run time to create new bean.
package com.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
#Configuration
public class ConfigClass {
#Bean(name="RC")
public JavaClient getClient(#Autowired Environment env)
{
String sName=env.getProperty("RCSName");
String aUrl=env.getProperty("RCAUrl");
String dUrl=env.getProperty("RCDUrl");
return new JavaClient(sName,aUrl,dUrl);
}
#Bean(name="O")
public JavaClient getOClient(#Autowired Environment env)
{
String sName=env.getProperty("OSName");
String aUrl=env.getProperty("OAUrl");
String dUrl=env.getProperty("ODUrl");
return new JavaClient(sName,aUrl,dUrl);
}
}
Now it is creating 2 beans as per above code. Expectation: How to combine getClient & getOClient into single method, but the property to be supplied in a loop to create multiple beans of same type JavaClient
I have modified my ConfigClass as below & created ApplicationContextAware to inject beans by reading file properties.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.EnableScheduling;
#Configuration
#EnableScheduling
public class ConfigClass {
#Bean
#Scope("prototype")
public JavaClient getClient(String sName,String aUrl,String dUrl)
{
return new JavaClient(sName,aUrl,dUrl);
}
}
Then Have created ApplicationContextAware to create Beans.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
#PostConstruct
public void init()
{
try {
String fileName = "Url.txt";
Resource resource = new ClassPathResource(fileName);
File file = resource.getFile();
List<String> lines = Files.readAllLines(file.toPath());
for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
String line = (String) iterator.next();
String[] s = line.split(",");
applicationContext.getBean(JavaClient.class,s[0], s[1], s[2]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

convert a simple maven websocket tyrus project to javac command line

Out of curiosity I'd like to build a simple websockets tyrus server chat project with javac and libraries in -classpath.
Yes, I know that this is not the standard way (mvn is), but I'm doing it as a proof of concept. I manage to get project built but it throws a NullPointerException
Code is:
ChatMain.java:
package chat;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.websocket.DeploymentException;
import org.glassfish.tyrus.server.Server;
public class ChatMain {
public static void main(String[] args) throws DeploymentException,
IOException {
Map<String, Object> properties = Collections.emptyMap();
Server server = new Server("localhost", 8080, "/ws", properties,
ChatEndPoint.class);
System.out.println(server);
try {
server.start();
System.in.read();
} finally {
server.stop();
}
}
}
ChatEndPoint.java:
package chat;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.json.*;
#ServerEndpoint("/chat")
public class ChatEndPoint {
private static Map<String, Session> peers = Collections.synchronizedMap(new HashMap<String, Session>());
#OnOpen
public void onOpen(Session peer) {
System.out.println("onOpen");
}
#OnMessage
public void onMessage(String message, Session peer) throws IOException {
System.out.println("onMessage");
JSONObject json = new JSONObject(message);
String nick = (String) json.get("nick");
boolean newClient = json.has("action") && ((String) json.get("action")).equals("add");
synchronized(peers) {
// Iterate over the connected sessions
// and broadcast the received message
for (Map.Entry<String, Session> entry : peers.entrySet()) {
if (newClient) {
JSONObject json1 = new JSONObject(json, new String[] { "action" }).put("nick", entry.getKey());
peer.getBasicRemote().sendText(json1.toString());
}
entry.getValue().getBasicRemote().sendText(message);
}
}
if (newClient)
peers.put(nick, peer);
}
#OnClose
public void onClose(Session peer) {
System.out.println("onClose");
peers.values().remove(peer);
}
}
javac line:
javac -cp chat/javax.websocket-api-1.1.jar:chat/tyrus-server-1.12.jar:chat/tyrus-spi-1.12.jar:chat/tyrus-core-1.12.jar:chat/grizzly-framework-2.3.22.jar:chat/grizzly-http-server-2.3.22.jar:chat/tyrus-container-grizzly-server-1.12.jar:chat/json.jar: chat/ChatMain.java
java line:
java -cp chat/javax.websocket-api-1.1.jar:chat/tyrus-server-1.12.jar:chat/tyrus-spi-1.12.jar:chat/tyrus-core-1.12.jar:chat/grizzly-framework-2.3.22.jar:chat/grizzly-http-server-2.3.22.jar:chat/tyrus-container-grizzly-server-1.12.jar:chat/json.jar: chat.ChatMain
runtime error:
Exception in thread "main" java.lang.NullPointerException
at org.glassfish.tyrus.container.grizzly.server.GrizzlyServerContainer$1.stop(GrizzlyServerContainer.java:228)
at org.glassfish.tyrus.server.Server.stop(Server.java:231)
at chat.ChatMain.main(ChatMain.java:23)
I understand that this can be done
Interestingly, more libraries are needed. If I include all libraries in websocket-ri-archive-1.12.zip bundle, it works well. Definitely, a builder is the way to go

Resources