Define what value to put in a Spring cache - java

Using Spring cache abstraction, I want to cache the results of itemExists method calls. When an item is inserted by insertItem method, I want to put the value true in the cache.
class MyServiceImpl implements MyService {
private static final String CACHE_EXISTS_NAME = "existsCache";
#Override
#Cacheable(CACHE_EXISTS_NAME)
public boolean itemExists(Long id) {
// access the repository the check whether the item exists
}
#Override
#CachePut(cacheNames = CACHE_EXISTS_NAME, key = "#item.id")
public Item insertItem(Item item) {
...
}
}
How can I achieve what I need?

Its not supported. A work around could be to define #Cacheable on say your dao's finder method and then call it from itemExists method:
In the DAO:
#Cacheable(CACHE_EXISTS_NAME)
public Item findById(Long id) {
//..
}
In your service:
#Override
public boolean itemExists(Long id) {
if(null == dao.findById(id)) {
return false;
} else {
return true;
}
}
Note that since the method calls are proxied to support this, defining and calling finder inside the same object (service class in this case) will result in annotation being ignored.

I find your use case a bit disturbing. If you want to see if a particular item is in the cache, why don't you ask the cache rather than having those convoluted indirections? You could inject the Cache instance and asks for its content.
I guess that changing your code to the following might work but I really do not recommend that:
#Override
#CachePut(cacheNames = CACHE_EXISTS_NAME, key = "#item.id")
public boolean insertItem(Item item) {
...
return true;
}

Related

Using Hibernate Search 6 - How to prevent automatic indexing for a specific entity?

We would like to handle the indexing ourselves for one of our Hibernate Search entities.
Is it possible to disable auto indexing within Hibernate Search 6 for a specific entity? Similar to the older Hibernate Search global setting: hibernate.search.indexing_strategy = manual
I've searched through the documentation but haven't seen this mentioned.
hibernate.search.indexing_strategy = manual was for all entity types, not for a specific one.
The feature you're looking for has already been filed as HSEARCH-168, and is currently planned for Hibernate Search 6.2.
In the meantime, I think the best you can do would be to rely on a hack. It won't be as efficient as what we envision for HSEARCH-168, but it's a start:
Implement a RoutingBridge which, based on a switch, will either disable indexing completely (both adding and removing the entity from the index) or behave normally as if there was no routing bridge:
public class ManualIndexingRoutingBinder implements RoutingBinder {
private static volatile boolean indexingEnabled = false;
public static synchronized void withIndexingEnabled(Runnable runnable) {
indexingEnabled = true;
try {
runnable.run();
}
finally {
indexingEnabled = false;
}
}
#Override
public void bind(RoutingBindingContext context) {
context.dependencies()
.useRootOnly();
context.bridge(
Book.class,
new Bridge()
);
}
public static class Bridge implements RoutingBridge<Book> {
#Override
public void route(DocumentRoutes routes, Object entityIdentifier, Book indexedEntity,
RoutingBridgeRouteContext context) {
if ( indexingEnabled ) {
routes.addRoute();
}
else {
routes.notIndexed();
}
}
#Override
public void previousRoutes(DocumentRoutes routes, Object entityIdentifier, Book indexedEntity,
RoutingBridgeRouteContext context) {
if ( indexingEnabled ) {
// So that Hibernate Search will correctly delete entities if necessary.
// Only useful if you use SearchIndexingPlan for manual indexing,
// as the MassIndexer never deletes documents.
routes.addRoute();
}
else {
routes.notIndexed();
}
}
}
}
Apply that routing bridge to the entity types you want to control:
#Entity
#Indexed(routingBinder = #RoutingBinderRef(type = ManualIndexingRoutingBinder.class))
public class Book {
// ...
}
The routing bridge will effectively disable automatic indexing.
To index manually, do this:
ManualIndexingRoutingBinder.withIndexingEnabled(() -> {
Search.mapping(entityManagerFactory).scope(Book.class)
.massIndexer()
.startAndWait();
});
I didn't test this exactly, so please report back, but in principle this should work.

Play Framework 2.7: How to update session within a Action composition in Java

I'm trying to update an app from to Play 2.7. I see that now the access to the session object via Http.Context is deprecated. Instead I have to use the Http.Request object. Additionally before I could just change the Session object right away - now it seems like I have to create a new Session and add to the Result by myself. But how can I achieve this within an Action composition where I don't have access to the Result object?
An Action composition can look like:
public class VerboseAction extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Request req) {
...
return delegate.call(req);
}
}
I can't see how to add something to the Session here!
EDIT:
I couldn't find an easy solution but a workaround with a second action annotation. It's possible to access the Result object via .thenApply and attache the new Session object.
public CompletionStage<Result> call(Http.Request request) {
return delegate.call(request).thenApply(result -> {
Http.Session session = ... change the session
return result.withSession(session);
});
}
Still if someone has a better idea how to change the Session directly in the action composition please feel free to answer.
A session in cleared by withNewSession(). A new session is created when you add something with addingToSession(...), perhaps after a login. Here is my complete working code : I have 2 timestamp : one for the log file and one for an application timeout.
public class ActionCreator implements play.http.ActionCreator {
private final int msTimeout;
#Inject
public ActionCreator(Config config) {
this.msTimeout = config.getInt("application.msTimeout");
}
#Override
public Action<?> createAction(Http.Request request, Method actionMethod) {
return new Action.Simple() {
#Override
public CompletionStage<Result> call(Http.Request req) {
// add timestamp for the elapsed time in log
req.getHeaders().addHeader("x-log-timestamp", "" + System.currentTimeMillis());
// did a session timeout occur
boolean timeout = SessionUtils.isTimeout(req, msTimeout);
// apply current action
return delegate.call(req).thenApply(result -> {
// display some info in log
Utils.logInfo(req);
// return final result
if (timeout) {
return result.withNewSession();
} else if (SessionUtils.isOpen(req)) {
return result.addingToSession(req, "timestamp", "" + System.currentTimeMillis());
} else {
return result;
}
});
}
};
}
}

Custom Eclipse Source->generate Delegate

I want to create a Wrapper where the wrapped object may be null. If I go to Eclipses Source->generate Delegate Methods option I can choose a member and I'm able to select all methods. The Problem is in generating itself, because I become something like this:
class A {
public boolean aMethod(){
//some stuff
return true //may also return false
}
}
class Awrapper {
private A myA;
public boolean aMethod(){
return myA.aMethod();
}
}
but I need something like this:
class Awrapper {
private A myA;
public boolean aMethod(){
if (myA != null) {
myA.aMethod();
} else {
return false; // or the default for return type like 0 or null
}
}
}
There is an link stating:
"The format of the Delegate Method may be configured on the code Templates page, but all I can find is the generation of comments for delegates. Is this possible? What is taken from the code section to generate the delegate method, not the comment?

How to set the values in session?

If I'm getting empty session I need to setup some values to play the action class. So, here is the method
public SearchFilters getFilters() {
return (SearchFilters) getSession().get("Filters");
}
I would like to check the session, if it's null, then I need to set the some values over here.
public SearchFilters getFilters() {
if(getSession().get("Filters").equals(null)){
---- //How to set the values and return ?
}
return (SearchFilters) getSession().get("Filters");
}
Use the code:
public SearchFilters getFilters() {
if(getSession().get("Filters") == null){
//How to set the values
getSession().put("Filters", new Filters());
}
// and return.
return (SearchFilters) getSession().get("Filters");
}
assumed you have injected the session into the action via implementing SessionAware.
The value is a free hand object which contains no value, but you could create a constructor to it and pass the value directly.
getSession() will return a new session if an existing session is not found. So you don't need to worry about this one ever returning null. Take note though, there's no get() method under HttpSession, it's getAttribute().
So you can do this:
public SearchFilters getFilters() {
if(getSession().getAttribute("Filters") == null) {
getSession().setAttribute("Filters", new SearchFilters());
}
return (SearchFilters) getSession().getAttribute("Filters");
}

JSF Validation Error While Using Custom Converter

I am setting up a form using JSF (I'm pretty new at this) and I am getting a Validation Error: Value is not valid message on one of the fields. This field is actually a separate object (as I will show below) that has a custom converter.
Here is what I have (with non-relevant code removed):
I have a Citation class:
#ManagedBean(name="citation")
public class Citation {
private int id;
private Status status;
// getters and setters
}
I also have a Status class that you see referenced in the Citation class:
#ManagedBean(name="status")
public class Status {
private int id;
private String name;
// getters and setters
public List<Status> getAllStatuses() {
Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
session.clear();
Query query = session.createQuery("from Status");
List<Status> statuses = query.list();
try {
session.getTransaction().commit();
} catch (HibernateException e) {
// TODO: handle exception
session.getTransaction().rollback();
}
return statuses;
}
#Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Status)) return false;
if (this.id == ((Status)obj).getId()) {
return true;
} else {
return false;
}
}
#Override
public int hashCode() {
return this.name.hashCode();
}
}
Then for my form, I have:
<h:selectOneMenu id="citation_status" value="#{citation.status}">
<f:selectItems value="#{status.allStatuses} var="s" itemValue="#{s.id}" itemLabel="#{s.name}" />
</h:selectOneMenu>
<h:message for="citation_status" />
Lastly, for my converter, I have:
#FacesConverter(forClass=Status.class)
public class StatusConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// uses Hibernate to get the Status object (using a breakpoint in Eclipse, I have verified that this works)
// I can post this code if needed, but just trying to keep it short :)
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Status) value).getId());
}
}
Now when I get to my form and submit, I get the Validation Error next to the Status. I'm pretty new at this and thanks to #BalusC, I'm this far along.
Any help is greatly appreciated.
Validation Error: Value is not valid
In case of <h:selectOneMenu>, you will get this when error whenever the selected item does not match any of the items available in the list. I.e. selectedItem.equals(selectItem) has never returned true for any of the items.
Since it's apparently a custom object (the Status class), did you implement its Object#equals() (and #hashCode()) properly? You can if necessary let the IDE (Eclipse/Netbeans) autogenerate them.
See also:
Overriding equals and hashCode in Java
How to implement equals() in beans/entities
Update: after having a closer look at your code, it turns out that you're actually submitting #{s.id} instead of #{s} (the whole Status object). Fix the itemValue accordingly and it should work (if equals() is still doing its job properly).

Categories