Where does the filter for Ehcache 3 simple web page caching call the cache? - caching

I am trying to cache a simple web page in Ehcache. Thanks to some help from another SO post I discovered that I need to implement my own filter based on Ehcache 2 code. When I look at the filter I don't understand it. Where does it ever call the cache to return a value? Here is my implementation (quite possibly wrong):
package com.sentiment360.pulse.cache;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.Element;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.Configuration;
import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManager;
import org.ehcache.core.Ehcache;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.ehcache.xml.XmlConfiguration;
import javax.servlet.http.HttpServletRequest;
public class SimplePageCachingFilter implements CachingFilter {
public static final String DEFAULT_CACHE_NAME = "SimplePageCachingFilter";
private Logger LOG = Logger.getLogger(this.getClass().getName());
private String cacheName="basicCache";
protected String getCacheName() {
if (cacheName != null && cacheName.length() > 0) {
LOG.log(Level.INFO,"Using configured cacheName of {}.", cacheName);
return cacheName;
} else {
LOG.log(Level.INFO,"No cacheName configured. Using default of {}.", DEFAULT_CACHE_NAME);
return DEFAULT_CACHE_NAME;
}
}
protected CacheManager getCacheManager() {
return CacheManager.getInstance();
}
protected String calculateKey(HttpServletRequest httpRequest) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(httpRequest.getMethod()).append(httpRequest.getRequestURI()).append(httpRequest.getQueryString());
String key = stringBuffer.toString();
return key;
}
}

See in the super class.
But you do implements CachingFilter ?! Where is that interface? It does look like you were trying to "copy" the previous Ehcache's SimplePageCachingFilter, right? You would also need to port that abstract super class (and maybe read a little about javax.servlet.Filter, in case these aren't entirely clear...)
Now, you may also want to ping the dev team on the Ehcache Dev Google group about this. They should be able to provide pointers and then help with the implementation. Looks like a good idea for a future pull request! :)

Related

Spring-Boot Properties with nested Map

I have a application.properties file that needs some dynamic keys, which allow at least one level of nesting. In technical terms, the application starts and I am able to read those values, but the metadata doesn't seem to work correctly, because IntelliJ Ultimate is giving me some errors:
Cannot resolve property 'foo' in java.util.Map
# Static property
com.company.version=1.0
# Dynamic property, starting after products
com.company.products.first.foo=firstFoo
com.company.products.first.bar=firstBar
com.company.products.second.foo=SecondFoo
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
#Configuration
#ConfigurationProperties(prefix = "com.company")
public class Properties {
private String version;
private Map<String, Map<String, ArrayList<String>>> products = new HashMap<>();
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Map<String, Map<String, ArrayList<String>>> getProducts() {
return products;
}
public void setProducts(Map<String, Map<String, ArrayList<String>>> products) {
this.products = products;
}
}
In additional I would like to know what the best practice would be to read those values. For "normal" static properties I used the Environment that I autowire in the constructor, but that is – as far as I know – unable to read the products map. Thats why I autowire my Properties class and use the getProducts function.
Thanks a lot for your help!
Might this work?
# Dynamic property, starting after products
com.company.products.first[foo]=firstFoo
com.company.products.first[bar]=firstBar
com.company.products.second[foo]=SecondFoo
If you want more than item in your array list do:
com.company.products.first[foo]=firstFoo,secondFoo
However, I've come massively unstuck in the past using deeply nested maps of maps. Maybe if you can declare a concrete class instead?
Also, I'd suggest using interfaces rather than concrete collections (e.g. List rather than ArrayList, or maybe even a Set to prevent duplicates?)

REST API return entire queried JSON

I do maintain an small REST API (SpringBoot) offering services to obtain data about ticketing from a small sized retailer. i.e.: you can send a request in order to get some information from a certain ticket (which has unique ID); the JSON response consists of a selection of fields from the unique ticket (which is stored as an unique document in Mongo DB).
Let say the API receives a request, then it would execute a query to Mongo DB, and then apply a projection to parse the queried data into a data model class, which in turn is finally parsed to a response JSON like, i.e.:
{
"ticketData": {
"retailerId": "023",
"ticketId": "09834723469324",
"ticketDate": "2021-06-20"
},
"buyerData": {
"buyerId": "LN4382"
}
}
Well, I am now required to return the entire queried JSON (that is, a JSON containing the whole ticket information, that has a lot of fields). ¿Is there any way to achieve this without creating a data model class with tens or hundreds of properties to match the stored ticket JSON? Even if I specify the API response using YAML and then use a codegen tool, is a lot of tedious work, and whenever the ticket JSON format evolves, I would need to change my DAO and response.
I just would like to send the original Mongo stored JSON and hand it back to the API client. Is there any way to achieve that?
You can utilize Jackson ObjectMapper which Spring already uses to serialize and deserialize JSON.
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
#RestController
public class HelloWorldController {
private final ObjectMapper objectMapper;
public HelloWorldController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#GetMapping("/jsonList")
public ResponseEntity<List<JsonNode>> getJsonList() {
List<String> data = List.of("{\"number\": 1}",
"{\"number\": 2}",
"{\"number\": 3}");
List<JsonNode> nodes = toJsonNodeList(data);
return ResponseEntity.ok(nodes);
}
private List<JsonNode> toJsonNodeList(List<String> strings) {
List<JsonNode> nodes = new ArrayList<>();
for (String s : strings) {
try {
JsonNode node = this.objectMapper.readTree(s);
nodes.add(node);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
return nodes;
}
}

Micronaut-Data JDBC - Multiple Dialects for Test and Production

The Mirconaut docs on JDBC repositories clearly tells us we have to create a test repository to test against another dialect. I think this will be manageable (e.g. Postgres for production and H2 for test).
The problem is I have to repeat my methods (e.g. find()) in the test repository. I have a book repository and a test repository:
#JdbcRepository(dialect = Dialect.POSTGRES)
interface BookRepository extends CrudRepository<Book, Long> {
Optional<Book> find(String title);
}
#JdbcRepository(dialect = Dialect.H2)
#Replaces(bean = BookRepository)
#Requires(env = ["test"])
interface TestBookRepository extends BookRepository {
// Optional<Book> find(String title);
// Required to make the find() method appear in the TestBookRepository
}
To make the find() method available in the TestBookRepository, I had to repeat the method (see commented line above).
Is there a better way to avoid repeating myself? The methods from the CrudRepository interface are available in the TestBookRepository without problems. Why is the find() method not treated the same?
BTW, I don't want to mock the test repository. I want to test the repository 'logic' injected by Micronaut-Data against an SQL database.
This is for Micronaut Data 1.0.0.M5, using Groovy for the source.
To make the find() method available in the TestBookRepository, I had
to repeat the method (see commented line above).
I cannot reproduce that behavior. In order for that to be the case I think the java compiler would need to have a bug in it that caused that.
See the project at https://github.com/jeffbrown/mikehoustonrepository.
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/main/java/mikehoustonrepository/BookRepository.java
// src/main/java/mikehoustonrepository/BookRepository.java
package mikehoustonrepository;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;
import java.util.Optional;
#JdbcRepository(dialect = Dialect.POSTGRES)
public interface BookRepository extends CrudRepository<Book, Long> {
Optional<Book> find(String title);
}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/test/java/mikehoustonrepository/TestBookRepository.java
// src/test/java/mikehoustonrepository/TestBookRepository.java
package mikehoustonrepository;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
#JdbcRepository(dialect = Dialect.H2)
#Replaces(BookRepository.class)
public interface TestBookRepository extends BookRepository{}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/main/java/mikehoustonrepository/BookController.java
package mikehoustonrepository;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import java.util.Optional;
#Controller("/books")
public class BookController {
private final BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#Get("/")
public Iterable<Book> index() {
return bookRepository.findAll();
}
#Post("/{title}/{author}")
public Book create(String title, String author) {
return bookRepository.save(new Book(title, author));
}
#Get("/find/{title}")
public Optional<Book> findByTitle(String title) {
return bookRepository.find(title);
}
}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/test/java/mikehoustonrepository/BookControllerTest.java
package mikehoustonrepository;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
#MicronautTest
public class BookControllerTest {
#Inject
BookClient bookClient;
#Test
public void testFind() throws Exception {
Optional<Book> book = bookClient.find("The Nature Of Necessity");
assertFalse(book.isPresent());
bookClient.create("The Nature Of Necessity", "Alvin Plantinga");
book = bookClient.find("The Nature Of Necessity");
assertTrue(book.isPresent());
}
}
#Client(value="/", path = "/books")
interface BookClient {
#Post("/{title}/{author}")
Book create(String title, String author);
#Get("/")
List<Book> list();
#Get("/find/{title}")
Optional<Book> find(String title);
}
That test passes.
You can see that a different repository is being used for test (TestBookRepository) that is used for other environments (BookRepository).
I hope that helps.
You can utilise Micronaut environments to create different environment configuration for test and production
and configure respective datasource configuration in application-test.yml
and use that datasource for tests
Micronaut Environments from docs
After some more work, I found another way to solve the original problem. You can define a base interface class that just has the methods you need. Then implement concrete classes for the dialect(s) you need. This allows one type of DB for test and one for production.
interface OrderRepository extends BaseRepository, CrudRepository<Order, UUID> {
#Join(value = "product", type = Join.Type.LEFT_FETCH)
Optional<Order> findById(UUID uuid)
}
#JdbcRepository(dialect = Dialect.H2)
#Requires(env = ["test"])
interface OrderRepositoryH2 extends OrderRepository, CrudRepository<Order, UUID> {
}
#JdbcRepository(dialect = Dialect.POSTGRES)
#Requires(env = ["dev"])
interface OrderRepositoryPostgres extends OrderRepository, CrudRepository<Order, UUID> {
}
No methods are needed in the OrderRepositoryH2 interface. Micronaut-data uses the methods from the parent interface fine. The trick is to not use the #JdbcRepository annotation in the parent interface.
You can create any other dialects needed, but you have to make sure the #Requires annotation results in only one bean for any given mode.
I plan to use H2 for testing, with an option to use the Postgres dialect for special test runs when needed.
Sorry for any confusion on the question and comments.
(I decided to mark this as the answer since it solves the original problem).

"Cannot resolve symbol" error while implementing method to get data from logged jhipster user

I am a newbie at Java programming and Jhipster framework and maybe my question is an annoying one, but despite the many topics I read on it I can't solve my question. So, I am asking for some help.
In this topic getting the current logged in user in JHipster I found this piece of code:
final Optional<User> isUser = userService.getUserWithAuthorities();
if(!isUser.isPresent()) {
log.error("User is not logged in");
return new Shinything()
}
final User user = isUser.get();
// continue with the user
which is exactly what I need in my class NjdUserConfiguration.java: this class contains a field (User) user and I aim to get NjdUserConfiguration retrieved by logged in user login.
So, firstly, I add to NjdUserConfigurationRepository.java this query annotation:
#Query("select njd_user_configuration from NjdUserConfiguration njd_user_configuration where njd_user_configuration.user =:user")
Optional<NjdUserConfiguration> findOneByUser(#Param("user") User user);
Secondly, I create NjdUserConfigurationService.java like this:
package it.tal.app.service;
import it.tal.app.domain.NjdUserConfiguration;
import it.tal.app.domain.User;
import it.tal.app.repository.NjdUserConfigurationRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
/**
* Service Implementation for managing NdjUserConfiguration.
*/
#Service
#Transactional
public class NjdUserConfigurationService {
private final Logger log = LoggerFactory.getLogger(NjdUserConfigurationService.class);
private final NjdUserConfigurationRepository NjdUserConfigurationRepository;
public NjdUserConfigurationService(NjdUserConfigurationRepository njdUserConfigurationRepository) {
this.NjdUserConfigurationRepository = njdUserConfigurationRepository;
}
/**
* Get one NdjUserConfiguration by user.
*
* #param user the user of the entity
* #return the entity
*/
#Transactional(readOnly = true)
public Optional<NjdUserConfiguration> findOneByUser(User user) {
log.debug("Request to get NdjUserConfiguration : {}", user);
return NjdUserConfigurationRepository.findOneByUser(user);
}
}
Thirdly, I tried to used it in NjdUserConfiguration.java mocking the original code in this new method:
public NjdUserConfiguration getCurrentUser() {
Optional<User> isUser = new UserService.getUserWithAuthorities();
if(isUser.isPresent()) {
//final User user = isUser.get();
return new NjdUserConfigurationService.getOneByUser(isUser.get());
} else {
return null;
}
}
No matter my efforts both getUserWithAuthorities() and getOneByUser(isUser.get()) result in "cannot resolve symbol getUserWithAuthorities()" and "cannot resolve symbol getOneByUser()", though both
import it.tal.app.service.NjdUserConfigurationService;
import it.tal.app.service.UserService;
are present. What did I do so badly or what am I missing?
Thank you
You misuse new operator. Instead of
return new UserService.getUserWithAuthorities();
use
return UserService.getUserWithAuthorities();
assuming UserService is an injected bean.
BTW, java code conventions recommend naming variables so they start with lowercase letter.

Is it possible to connect to two different buckets of couchbase in spring boot

I am trying to connect to two different buckets in couchbase using spring boot. But in a single spring boot application the database config only takes a single bucket name.
Is it possible to connect to more than one couchbase bucket in spring-boot?
So it seems you want to use Spring Data Couchbase from within a Spring Boot application, and have (at least) two different repositories backed by two different Bucket?
You'll have to customize your Spring Data configuration programmatically (as opposed to letting Spring Boot do all the heavy lifting), but that's possible.
Spring Boot creates a CouchbaseConfigurer through which it creates default Cluster and Bucket (as tuned in the properties file).
If you have a CouchbaseRepository on your classpath, it'll also attempt to configure Spring Data by instantiating a SpringBootCouchbaseDataConfiguration class.
You can customize that by extending the SpringBootCouchbaseDataConfiguration above in your project, marking it as #Configuration
Once you're ready to customize the Spring Data configuration programmatically, what you need is to create a second Bucket bean, a second CouchbaseTemplate that uses that bucket, and then instruct Spring Data Couchbase on which template to use with which Repository.
To that end, there is a configureRepositoryOperationsMapping(...) method. You can use the parameter of this method as a builder to:
link a specific Repository interface to a CouchbaseTemplate: map
say that any repo with a specific entity type should use a given template: mapEntity
even redefine the default template to use (initially the one created by Spring Boot): setDefault.
This second part is explained in the Spring Data Couchbase documentation.
Probably what you are trying to say is that Spring boot provides pre-defined properties that you can modify, such as: couchbase.cluster.bucket that takes single value and you want to connect to two or more buckets.
In case you will not find a better solution, I can point you to a slightly different approach, and that is to setup your own couchbase connection manager that you can inject anywhere you need.
Here is the example of such #Service that will provider you with two connections to different buckets.
You can modify to suite your needs, it is very small.
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
#Service
public class CouchbaseConnectionManager {
private static final int TIMEOUT = 100000;
#Value("#{configProp['couchbase.nodes']}")
private List<String> nodes = new ArrayList<String>();
#Value("#{configProp['couchbase.binary.bucketname']}")
private String binaryBucketName;
#Value("#{configProp['couchbase.nonbinary.bucketname']}")
private String nonbinaryBucketName;
#Value("#{configProp['couchbase.password']}")
private String password;
private Bucket binaryBucket;
private Bucket nonbinaryBucket;
private Cluster cluster;
private static final Logger log = Logger.getLogger(CouchbaseConnectionManager.class);
#PostConstruct
public void createSession() {
if (nodes != null && nodes.size() != 0) {
try {
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.builder().connectTimeout(TIMEOUT).build();
cluster = CouchbaseCluster.create(env, nodes);
binaryBucket = cluster.openBucket(binaryBucketName, password);
nonbinaryBucket = cluster.openBucket(nonbinaryBucketName, password);
log.info(GOT_A_CONNECTION_TO_COUCHBASE_BUCKETS + binaryBucket + " " + nonbinaryBucket);
} catch (Exception e) {
log.warn(UNABLE_TO_GET_CONNECTION_TO_COUCHBASE_BUCKETS);
}
} else {
log.warn(COUCH_NOT_CONFIGURED);
}
}
#PreDestroy
public void preDestroy() {
if (cluster != null) {
cluster.disconnect();
log.info(SUCCESSFULLY_DISCONNECTED_FROM_COUCHBASE);
}
}
public Bucket getBinaryBucket() {
return binaryBucket;
}
public Bucket getNonbinaryBucket() {
return nonbinaryBucket;
}
private static final String SUCCESSFULLY_DISCONNECTED_FROM_COUCHBASE = "Successfully disconnected from couchbase";
private static final String GOT_A_CONNECTION_TO_COUCHBASE_BUCKETS = "Got a connection to couchbase buckets: ";
private static final String COUCH_NOT_CONFIGURED = "COUCH not configured!!";
private static final String UNABLE_TO_GET_CONNECTION_TO_COUCHBASE_BUCKETS = "Unable to get connection to couchbase buckets";
}
I followed Simon's approach and extended the org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration for the #Configuration instead of SpringBootCouchbaseDataConfiguration.
Also, a point worth mentioning is that for some reason having separate Repository packages and having its own #Configuration doesn't really work. I struggled a great deal to try and make it work and eventually settled on having all the Repositories in a single package and ended up having something like the below to map the Entities and Templates.
baseMapping.mapEntity(Prime.class, noSQLSearchDBTemplate())
.mapEntity(PrimeDetailsMaster.class, noSQLSearchDBTemplate())
.mapEntity(HostDetailsMaster.class, noSQLSearchDBTemplate())
.mapEntity(Events.class, eventsTemplate())
.mapEntity(EventRulesMaster.class, eventsTemplate());

Resources