NullPointerException when bucket.defaultCollection() is called Couchbase SDK3 - couchbase

Getting NullPointerException when trying the below code
public class SalesCouchbaseDao {
#Resource
private Cluster cluster;
#Autowired
#Qualifier("salesBucket")
private Bucket bucket;
private final Logger LOG = LoggerFactory.getLogger(SalesCouchbaseDao.class);
private Collection collection = bucket.defaultCollection();
The NullPointerException is returned on this line
private Collection collection = bucket.defaultCollection();
As per the documentation, this is the way to get default Collection however, i am getting null pointer exception
This is the CouchbaseConfigClass
#Configuration
public class CouchbaseConfig {
private static final Logger LOG = LoggerFactory.getLogger(CouchbaseConfig.class);
#Autowired
private CipherHelper cipherHelper;
/** Common */
#Value("${couchbase.cluster.host}")
private String host;
/** sales bucket */
#Value("${couchbase.cluster.bucket.sales}")
private String salesBucket;
#Value("${couchbase.cluster.query.timeout}")
private Long queryTimeout;
#Value("${couchbase.cluster.view.timeout}")
private Long viewTimeout;
#Value("${couchbase.cluster.username}")
private String clusterUserName;
#Value("${couchbase.cluster.password}")
private String clusterPassword;
private static final long RECONNECT_DELAY = 60L;
/**
* couchbaseCluster.
*
* #throws BadPaddingException
* #throws IllegalBlockSizeException
* #throws NoSuchPaddingException
* #throws NoSuchAlgorithmException
* #throws InvalidKeyException
*/
#Bean
public Cluster couchbaseCluster() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
ClusterEnvironment clusterEnvironment = ClusterEnvironment
.builder()
.timeoutConfig(TimeoutConfig.builder()
.queryTimeout(Duration.ofMillis(TimeUnit.SECONDS.toMillis(queryTimeout)))
.viewTimeout(Duration.ofMillis(TimeUnit.SECONDS.toMillis(viewTimeout))))
.build();
return Cluster.connect(host, ClusterOptions
.clusterOptions(clusterUserName.trim(), cipherHelper.decrypt(clusterPassword.trim()))
.environment(clusterEnvironment));
}
#Bean
#Qualifier(value = "salesBucket")
public Bucket salesBucket(final Cluster couchbaseCluster) {
return this.getBucket(couchbaseCluster,salesBucket);
}
private Bucket getBucket(final Cluster couchbaseCluster, final String bucketName) {
Bucket bucket = couchbaseCluster.bucket(bucketName);
bucket.waitUntilReady(Duration.ofSeconds(10));
return bucket;
}
}
The above is the couchbase configuration class. The CouchbaseConfig is in a separate module.
I am using couchbase sdk 3
How can i fix this code ?

Field initializers are invoked before Spring auto-wiring happens. The NullPointerException happens because bucket is still null when the collection field is initialized.
One option is to use a bean lifecycle callback to get the collection after the bucket has been auto-wired:
private Collection collection;
#PostConstruct
private void initCollection() {
this.collection = bucket.defaultCollection();
}
Another option would be to treat the collection as a bean, and auto-wire the collection instead of the bucket.
A third option would be to use constructor injection or setter injection, passing the bucket to the constructor or setter, and initializing the collection inside the constructor/setter. Refer to spring #Autowire property vs setter.

Related

Status 400 and Error deserializing List of Objects. No default constructor found

I have this Spring Repository:
public interface MessageRepository extends CrudRepository<MessageObject, String>{
public List<MessageObject> findByEmisorOrDestinatario(String emisor, String destinatario);
}
My DAO is:
#Entity
#Table(name = "messages")
public class MessageObject implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String emisor;
private String destinatario;
private String mensaje;
private String tipo;
#JsonFormat(pattern="yyyy-MM-dd")
private LocalDate fecha;
private String id_housing;
public MessageObject() {
}
Now in my Controller I want to receive the Get request and search in my DB so:
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public List<MessageObject> enviados (#RequestParam("mail") String mail) {
return daoMsg.findByEmisorOrDestinatario(mail, mail);
}
}
Now I can call the service from my client, so:
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient();
WebTarget webResource = client.target("http://localhost:8082").path("/Mensajes").queryParam(mail);
Invocation.Builder invocationBuilder = webResource.request(MediaType.APPLICATION_JSON);
Response respuesta = invocationBuilder.get();
int status = respuesta.getStatus();
System.out.println(status);
MessageObject[] listMessages = respuesta.readEntity(MessageObject[].class);
Problems: I'm receiving a 400 status code. Also an error deserializing entityRead. Doing the request with Postman returns no errors and return the list of objects in JSON format.
StackTrace:
javax.ws.rs.ProcessingException: Error deserializing object from entity
stream. Caused by: javax.json.bind.JsonbException: Can't create instance of
a class: class [LMessages.MessageObject;
No default constructor found. Caused by: java.lang.NoSuchMethodException:
[LMessages.MessageObject;.<init>()
Question: how can I know where is my code failing? am I using the service invocation well?
Things I tried: changing Mediatype to GenericType
EDIT I tried removing the / from the path, still getting status 400
Solved. Problem was I was using .queryparam without key-value structure. So changing .queryparam(mail) to .queryparam("mail", mail) solved it.
Try to test with this :
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
#ReponseBody
public ResponseEntity<List<MessageObject>> enviados (#RequestParam("mail") String mail) {
return new ResponseEntity<>(daoMsg.findByEmisorOrDestinatario(mail, mail), HttpStatus.OK);
}

Unit Test class not running properly - Mocking Interfaces

I have a simple Controller class like below:-
#RestController
public class CrawlerAppController {
private static final Logger LOGGER = LoggerFactory.getLogger(CrawlerAppController.class);
#Autowired
private CrawlerServiceInterface crawlerService;
/* The response time of the crawling operation is directly proportional to the no of pages
* we want to crawl. Keeping a default value of 10 so we can view the results quicker.
* author: Arunava Paul
*/
#RequestMapping(value = "/crawl", method = { RequestMethod.GET })
public Object crawlUrl(#RequestParam(value = "URL") String URL,
#RequestParam(value = "max", defaultValue = "10") int maxPages) throws Exception {
if(!URL.startsWith("https://"))
URL="https://"+URL;
LOGGER.info("Request Received. Domain "+URL+" Pages to be Crawled "+maxPages);
return crawlerService.crawlService(URL, maxPages);
}
}
I have written a Junit class like below:-
#RunWith(PowerMockRunner.class)
public class CrawlerAppControllerTest {
Object obj=new Object();
#Spy
#InjectMocks
private CrawlerServiceInterface crawlerService = Mockito.any(CrawlerService.class);
#InjectMocks
CrawlerAppController appController = new CrawlerAppController();
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testController() throws Exception {
when(crawlerService.crawlService("https://vogella.com", 20)).thenReturn(obj);
assertEquals(appController.crawlUrl("vogella.com",20), obj);
}
}
It's always going into the Service class and the when statement is not running.
Can someone please advise what have I done wrong. Below error comes if I run Junit.
You should declare crawlerService like this:
#Mock
private CrawlerServiceInterface crawlerService;
The declaration of crawlerService in the test class should be:
#Mock
private CrawlerServiceInterface crawlerService;

How to get a static final property from a property file with spring boot

I've a spring boot project working on mysql DB. To encrypt some column I used the mysql function aes_encrypt, aes_decrypt and I stored my secret_key in a property file. Now to make some query on db I've to use in the crudrepository some native query like this.
#Query(value = "select * from a_table at where AES_DECRYPT(at.column1, +StaticClass.KEY+)= 'ABC'", nativeQuery = true)
public List<A>findByParameter();
I get this error "The value for annotation attribute Query.value must be a constant expression"
But the property KEY is already a FINAL property instantiate in this way
public class StaticClass {
static {
Properties prop = new Properties();
InputStream input;
try {
input = new FileInputStream("application.properties");
prop.load(input);
} catch (IOException e) {
logger.error("Error");
}
if(prop.getProperty("property1")==null) {
logger.error("property not found");
}
KEY=prop.getProperty("property1", "");
}
public static final String KEY;
One solution is to store the key in the code, but i need to keep the key in a property file. How can solve this problem?
Updated 1
I tried also in this way
public class StaticClass {
#Value(value="property1")
public static final String KEY;
}
You can create for this constants like below.
public enum Foo {
FOO("app.foo"), FOO_BAR("app.foo.bar");
private Environment environment;
private final String propertyKey;
Foo(String propertyKey) {
this.propertyKey = propertyKey;
}
public String getValue() {
return environment.getProperty(propertyKey);
}
private void setEnvironment(Environment environment) {
this.environment = environment;
}
#Component
private static class EnvironmentInjector {
#Autowired
private Environment environment;
#PostConstruct
private void postConstruct() {
for (Foo fT : EnumSet.allOf(Foo.class))
fT.setEnvironment(environment);
}
}
}
then you can use like this :
Foo.FOO.getValue()
#Query(value = "select * from a_table at where AES_DECRYPT(at.column1, +Foo.FOO.getValue()+)= 'ABC'", nativeQuery = true)
public List<A>findByParameter();

Junits for classes extending QuartzJobBean

I have a Java class that extends QuartzJobBean and has been scheduled at a specific time through out the day.
public class ServiceJob extends QuartzJobBean {
#Override
protected void executeInternal(JobExecutionContext context) {
}
Can someone please help me understand how to create a Junit test case for this. How do I invoke the executeInternal() method in the test case.
Thanks for any help on this.
I create a solution for my working project, i agree to adarshdatt to solve it via importing config file that defined the bean. You can find a good tutorial about it at this blog post,
For future use I want to show how i solve it with Mocking, just use Mockito #Mock annotation with this way :
SessionConfirmationJob.java
public class SessionConfirmationJob extends QuartzJobBean {
#Autowired
private SessionService sessionService;
#Autowired
private TransactionService transactionService;
#Autowired
private SystemLogger systemLogger;
public static final String TOKEN = "token";
private SpringInjectQuartzJobBean springInjectQuartzJobBean;
public SessionService getSessionService() {
return sessionService;
}
public void setSessionService(SessionService sessionService) {
this.sessionService = sessionService;
}
public TransactionService getTransactionService() {
return transactionService;
}
public void setTransactionService(TransactionService transactionService) {
this.transactionService = transactionService;
}
public void setSpringInjectQuartzJobBean(SpringInjectQuartzJobBean springInjectQuartzJobBean) {
this.springInjectQuartzJobBean = springInjectQuartzJobBean;
}
public SystemLogger getSystemLogger() {
return systemLogger;
}
public void setSystemLogger(SystemLogger systemLogger) {
this.systemLogger = systemLogger;
}
#Override
protected void executeInternal(JobExecutionContext paramJobExecutionContext) throws JobExecutionException {
springInjectQuartzJobBean = new SpringInjectQuartzJobBean();
springInjectQuartzJobBean.injectQuartzJobToSpringApplicationContext(this);
String token = paramJobExecutionContext.getMergedJobDataMap().getString(TOKEN);
Session session = sessionService.getByToken(token);
if (session != null) {
if (session.getPaymentConfirmation() == null || session.getPaymentConfirmation() != true) {
Transaction transactionToBeRolledBack = transactionService.getRollBackTransactionOfPayment(session);
if (transactionToBeRolledBack != null) {
try {
transactionService.rollBackTransaction(transactionToBeRolledBack);
} catch (IOException e) {
systemLogger.logException("Exception while rolling back transaction", e);
}
session = sessionService.getByToken(token);
session.setStatus(SessionStatus.FI);
session.setPaymentConfirmation(false);
sessionService.saveOrUpdate(session);
}
}
}
}
}
This is the method i should write test and this is the testing class.
SessionConfirmationJobTest.java
#RunWith(MockitoJUnitRunner.class)
public class SessionConfirmationJobTest {
#Mock
private SessionService sessionService;
#Mock
private TransactionService transactionService;
#Mock
private JobExecutionContext ctx;
#Mock
private SpringInjectQuartzJobBean springInjectQuartzJobBean;
private JobDataMap mergedJobDataMap = new JobDataMap();
#Mock
private Scheduler scheduler;
private SessionConfirmationJob sessionConfirmationJob;
private String token = "payment token";
#Before
public void setUp() throws SchedulerException {
mergedJobDataMap.put(SessionConfirmationJob.TOKEN, token);
when(ctx.getMergedJobDataMap()).thenReturn(mergedJobDataMap);
when(ctx.getScheduler()).thenReturn(scheduler);
when(scheduler.getContext()).thenReturn(null);
sessionConfirmationJob = new SessionConfirmationJob();
sessionConfirmationJob.setSessionService(sessionService);
sessionConfirmationJob.setTransactionService(transactionService);
sessionConfirmationJob.setSpringInjectQuartzJobBean(springInjectQuartzJobBean);
}
/**
* Test payment confirmation when we have false payment confirmation
*
* #throws JobExecutionException
*/
#Test
public void testPaymentRollBackForFalseConfirmation() throws IOException, JobExecutionException {
Session session = new Session();
session.setStatus(SessionStatus.AC);
session.setPaymentConfirmation(false);
Transaction transaction = new Transaction();
transaction.setSession(session);
transaction.setType(TransactionType.SALE);
transaction.setStatus(TransactionStatus.AP);
when(sessionService.getByToken(token)).thenReturn(session);
when(transactionService.getRollBackTransactionOfPayment(session)).thenReturn(transaction);
when(transactionService.rollBackTransaction(transaction)).thenReturn(true);
sessionConfirmationJob.execute(ctx);
Assert.assertEquals(SessionStatus.FI, session.getStatus());
Assert.assertFalse(session.getPaymentConfirmation());
verify(sessionService).saveOrUpdate(session);
}
}
Before mock the Schedular object i get NullPointerException at pvs.addPropertyValues(context.getScheduler().getContext()); after i mock schedular it is solved and my test is passed. Below is the
org.springframework.scheduling.quartz.QuartzJobBean#execute(JobExecutionContext context) method. Actually executeInternal is protected so we must call execute method first then execute method is call executeInternal which is override at your implemented Job class(my demo it is SessionConfirmationJob).
QuartzJobBean.java
public abstract class QuartzJobBean implements Job {
/**
* This implementation applies the passed-in job data map as bean property
* values, and delegates to {#code executeInternal} afterwards.
* #see #executeInternal
*/
#Override
public final void execute(JobExecutionContext context) throws JobExecutionException {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValues(context.getScheduler().getContext());
pvs.addPropertyValues(context.getMergedJobDataMap());
bw.setPropertyValues(pvs, true);
}
catch (SchedulerException ex) {
throw new JobExecutionException(ex);
}
executeInternal(context);
}
/**
* Execute the actual job. The job data map will already have been
* applied as bean property values by execute. The contract is
* exactly the same as for the standard Quartz execute method.
* #see #execute
*/
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
}
If you have question don't hesitate to ask me via comments.

Jackson deserialization - with contained ArrayList<T>

Good day,
I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:
{
"error":null,
"object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}
I have two classes to represent the type (the outer ListResponse):
public class ListResponse {
public String error;
public ArrayList<ListItem> object;
public ListResponse() {
}
}
and (the inner ListItem):
public class ListItem {
#JsonProperty("Id")
public int id;
#JsonProperty("Class")
public String classType;
#JsonProperty("ClassID")
public String classId;
#JsonProperty("ListItemParent_ID")
public int parentId;
#JsonProperty("Item")
public String item;
#JsonProperty("Description")
public String description;
#JsonAnySetter
public void handleUnknown(String key, Object value) {}
public ListItem() {
}
}
The class that invokes and returns the JSON looks like this:
public class CitizenPlusService {
private Client client = null;
private WebResource service = null;
public CitizenPlusService() {
initializeService("http://localhost:59105/PlusService/");
}
private void initializeService(String baseURI) {
// Use the default client configuration.
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
// Add a logging filter to track communication between server and client.
client.addFilter(new LoggingFilter());
// Add the base URI
service = client.resource(UriBuilder.fromUri(baseURI).build());
}
public ListResponse getListItems(String id) throws Exception
{
ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);
return response;
}
}
The important call here is the getListItems method. Running the code in a test harness, produces the following:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader#49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])
Please assist.
Regards,
Carl-Peter Meyer
You may be missing a #JsonDeserialize attribute as the type information does get lost in generics at run-time. Also you should avoid using concrete classes for collections if you can.
public class ListResponse {
public String error;
#JsonDeserialize(as=ArrayList.class, contentAs=ListItem.class)
public List<ListItem> object;
}
Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).
I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:
public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
private final BeanProperty property;
/**
* Default constructor needed by Jackson to be able to call 'createContextual'.
* Beware, that the object created here will cause a NPE when used for deserializing!
*/
public JsonCollectionDeserializer() {
super(Collection.class);
this.property = null;
}
/**
* Constructor for the actual object to be used for deserializing.
*
* #param property this is the property/field which is to be serialized
*/
private JsonCollectionDeserializer(BeanProperty property) {
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new JsonCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
switch (jp.getCurrentToken()) {
case VALUE_STRING:
// value is a string but we want it to be something else: unescape the string and convert it
return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
default:
// continue as normal: find the correct deserializer for the type and call it
return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
}
}
}
Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.
In your example you would now have to annotate your collection field like so:
public class ListResponse {
public String error;
#JsonDeserialize(using = JsonCollectionDeserializer.class)
public ArrayList<ListItem> object;
public ListResponse() {}
}
And that should be it.
Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.
The register subTypes works!
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
}
public class Point implements Geometry{
private String type="Point";
....
}
public class Polygon implements Geometry{
private String type="Polygon";
....
}
public class LineString implements Geometry{
private String type="LineString";
....
}
GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
try {
geojson=mapper.readValue(source, GeoJson.class);
} catch (IOException e) {
e.printStackTrace();
}