spring boot #Cachable returning all fields of superclasses filled with null values - spring

we are facing a strange problem and I dont quit understand whats going on and hope someone else already had the same issue and has a clue what is going on.
We wrote a simple REST service making use of #Cachable:
#GetMapping(value = "/get/{" + PARAM_TENANT + "}/{" + PARAM_UID + "}")
#Cacheable(value = GET_ORDERS_BY_UID )
public GetOrdersResponseDto getOrdersByUid(#PathVariable final String tenant, #PathVariable final String uid) {
....
return new GetOrdersResponseDto(createCacheKey(), orderResponseDtos);
}
GetOrdersResponseDto consists of several fields. Some contain instances of custom classes, some lists of them and other simple primitive values.
When the GetOrdersResponseDto response is served from the cache all fields of objects that are stored inside a list AND are located in the objects superclass are filled with null values.
We are using hazelcast as the cache implementation. And our cache config is very basic:
#Component
public class HazelcastConfig extends Config {
#Autowired
public HazelcastConfig(final ConfigClient configClient) {
super();
final GroupConfig groupConfig = getGroupConfig();
final String name = configClient
.getConfigPropertyValueOrThrow("public", "com.orderservice.hazelcast.group.name");
groupConfig.setName("foogroup");
final String password = configClient
.getConfigPropertyValueOrThrow("public", "com.orderservice.hazelcast.group.password");
groupConfig.setPassword(password);
The response class looks as follows:
public class GetOrdersResponseDto implements Serializable {
private String cacheSerial;
private List<OrderResponseDto> orderResponseDtos;
}
And the problems occur only for fields of OrderResponseDto that are part of the super class of OrderResponseDto.
I hope someone can give us an hint what's the cause for this strange behaviour.
Edit: I found out, that the problem only occurs for objects that are stored inside lists...

This is Java behaviour. See https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html
If your object is serializable and extends an object that is not serializable, then instead of the NotSerializeException which would be useful, the fields of the parent object are only initialized which is why you have them as nulls.
You can prove this in a unit test.
Here's one to reuse - https://github.com/hazelcast/hazelcast-code-samples/blob/master/serialization/hazelcast-airlines/the-code/src/test/java/com/hazelcast/samples/serialization/hazelcast/airlines/V1FlightTest.java

Related

Providing default values for validation in SpringBoot

I want SpringBoot to be able to provide default values for fields that the user must enter. For example, I have something like this:
*Controller class*
#PostMapping("/test")
public ResponseEntity<> myMethod(#RequestBody #Valid MyContract contract) {}
*MyContract class*
#Valid
DataObject dataObject;
*DataObject class*
#Component
public class DataObject {
private #Value("${field1.default}") String field1Default;
private String field1
public String getField1() {
return (field1 == null ? field1Default : field1);
}
}
The DataObject class needs to be created on a per request basis. There are also other places in the code where it needs to be created on demand. So I imagine it needs to be a Prototype object. But I can't figure out how to get Spring to created it properly when it creates it for the request.
Update
I have read more about #RequstBody, e.g., https://www.javadevjournal.com/spring/spring-request-response-body/ and Should spring #RequestBody class be singleton or prototype?, which explains that the object is not a Component, but a simple POJO that gets the values from the Json request. So it seems that there is no way to inject #Values from the Spring application.properties file. Is there any other way around this? Or another suggested implementation?

#Value not set in one specific class

I'm fairly sure I'm being some kind of idiot, but for the life of me I can't see it.
I have a large Spring Boot 2.1 application that extensively uses injection of properties through the #Value annotation. This works great, has done for years. But there's one specific, brand-new object where I can't get the values set. They are always null.
I know the problem isn't with the values themselves, because some of the same values inject just fine into other objects. But I just can't see what's wrong with THIS object, and would be grateful for your eyeballs.
The values in this object (which is in the same directory and builds just fine) are always null:
#Service
public class SSOUtil {
private String domain = "https://login.microsoftonline.com/";
private String tenantId = "[deleted guid]";
public static String localEnvironment = "local";
public static String devEnvironment = "dev";
public static String testEnvironment = "test";
public static String prodEnvironment = "prod";
#Value("${actions.PROD.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetProd;
#Value("${actions.TEST.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetTest;
#Value("${actions.DEV.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetDev;
#Value("${actions.touchnet_azure_ad_client_id_dev}")
private String clientIdDev;
#Value("${actions.touchnet_azure_ad_client_id_test}")
private String clientIdTest;
#Value("${actions.touchnet_azure_ad_client_id_prod}")
private String clientIdProd;
#Value("${touchnet.redirectURLDev}")
private String redirectURLDev;
#Value("${touchnet.redirectURLTest}")
private String redirectURLTest;
#Value("${touchnet.redirectURLProd}")
private String redirectURLProd;
private String clientId;
private String clientSecret;
private String redirectURI;
public SSOUtil() {
this.redirectURI = redirectURLTest;
this.clientSecret = clientSecretTouchnetTest;
}
public String getADLoginURL() {
String returnURL = "";
System.out.println(clientIdTest); // always prints null
}
}
The values in this object work just fine, though, and note that one of them is the same #Value as in the other class:
#Service
public class LibraryHelpServiceBean implements LibraryHelpService {
private CourseServiceBean courseServiceBean;
private final RestTemplate restTemplate;
#Value("${actions.libraryhelp_lti_api_key}")
private String apikey;
#Value("${actions.touchnet_azure_ad_client_id_test}")
String clientIdTest;
public LibraryHelpServiceBean(CourseServiceBean courseServiceBean, RestTemplateBuilder restTemplateBuilder) {
this.courseServiceBean = courseServiceBean;
this.restTemplate = restTemplateBuilder.build();
}
public void doesValueWork() {
this.apikey = this.apikey;
System.out.println(this.clientIdTest); // always prints correct value, a guid
}
}
Both objects are initialized in a similar way: either directly or indirectly through the #Autowired annotation in other objects that I use (and which work fine, and have worked fine for ages). Here's the creation of SSOUtil (my problem class):
#RestController
#RequestMapping(value = "/web")
public class SSOLandingController {
#Autowired
private SSOUtil ssoUtil;
[rest of class omitted]
}
And here's the creation of LibraryHelpServiceBean, which is working fine and has all #Values populate correctly:
#Service
public class LibraryHelpStreamServiceBean implements LibraryHelpStreamService {
private LibraryHelpServiceBean libraryHelpServiceBean;
public LibraryHelpStreamServiceBean(LibraryHelpServiceBean libraryHelpServiceBean){
this.libraryHelpServiceBean = libraryHelpServiceBean;
}
}
I have already tried changing the class annotation for SSOUtil from #Service to #Component (and #Configuration, just for the heck of it).
What could be causing the #Values in SSOUtil to come back null even though some of those same #Values populate just fine in other classes?
I'm convinced that I'm missing something obvious. I'm hoping it's something small like a typo. I'm nervous that it's something big, like I've completely misunderstood how Spring IOC works for the past several years.
Thanks for your help.
I tested your case on my computer, but I'm not able to reproduce your problem. When things like this is happening, try something very simple like this
package no.mycompany.springbootapp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class SSOUtil2 {
#Value("${actions.touchnet_azure_ad_client_id_test}")
private String clientIdTest;
}
Inject this component into your controller, set a breakpoint inside your controller method and inspect the injected instance.
My experience is that some unexplainable cases I've been involved in here on SO, were solved by cleaning the build or wiping the .m2-folder.

How can I properly override a method declared in an abstract generic restcontroller?

I'm having some trouble implementing a function over some pre-existing code.
Other programmers working on this project previously defined a genric abstract "restcontroller" (it's not actually annotated as #RestController but it's meant to be extended by classes with that annotation)
public abstract class AbstractController<T extends AbstractEntity, R extends JpaRepository<T, Integer>> {
#GetMapping(value = "/getall")
public Paging<T> getAll(#RequestParam Integer itemsPerPage,
#RequestParam Integer pageIndex,
#RequestParam Map<String, String> filters,
#Autowired Consumer consumer) {
//Fetch entities of type T from repository R and return them
}
//other generic crud operations
}
This class is usually extended by concrete controllers that simply define other operations on their specific types, but do no alter generic crud operations.
What I want to do is extend this class, but override the getAll method, like this:
#RestController
#RequestMapping("/api/tasks")
public class TaskController extends AbstractController<Task, TaskRepository> {
#Override
public Paging<Task> getAll(Integer itemsPerPage, Integer pageIndex, Map<String, String> filters, Consumer consumer) {
LoggerFactory.getLogger(LazyTaskController.class).log("function called successfully!");
Paging<Task> paging = super.getAll(itemsPerPage, pageIndex, filters, consumer);
//do things with return value before returning
return paging;
}
}
If I call BASEURL/api/tasks/getall?itemsPerPage=25&pageIndex=0 without overriding the getAll method, the parameters are wired correctly (the Map contains two values, itemsPerPage and pageIndex, as expected, and consumer contains a concrete implementation of the intercace Consumer).
However if I do override it, the Map for some reason contains two values, one with key "consumer" and type Proxy, and another with key "org.springframework.validation.BindingResult.consumer" and value of type BeanPropertyBindingResult; and consumer contains a Proxy.
I suppose the #Override interferes with the autowiring of Consumer, but I can't figure out how to properly achieve what I have in mind (manipulating the results of getAll before returning them).
Thank you in advance
Nevermind, I solved it.
The problem with the Map was solved by adding #RequestParam and #Autowired annotations to the overridden method parameters as well.
The problem with the Consumer concrete type was somehow solved by applying a custom annotation that I found on another class in the codebase, I'm still not sure about what that annotation does but at least I know what to look for now.

OData (Olingo) "inhibit" endpoint

My question is about what is best way to inhibit an endpoint that is automatically provided by Olingo?
I am playing with a simple app based on Spring boot and using Apache Olingo.On short, this is my servlet registration:
#Configuration
public class CxfServletUtil{
#Bean
public ServletRegistrationBean getODataServletRegistrationBean() {
ServletRegistrationBean odataServletRegistrationBean = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(), "/user.svc/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory", "com.olingotest.core.CustomODataJPAServiceFactory");
odataServletRegistrationBean.setInitParameters(initParameters);
return odataServletRegistrationBean;
} ...
where my ODataJPAServiceFactory is
#Component
public class CustomODataJPAServiceFactory extends ODataJPAServiceFactory implements ApplicationContextAware {
private static ApplicationContext context;
private static final String PERSISTENCE_UNIT_NAME = "myPersistenceUnit";
private static final String ENTITY_MANAGER_FACTORY_ID = "entityManagerFactory";
#Override
public ODataJPAContext initializeODataJPAContext()
throws ODataJPARuntimeException {
ODataJPAContext oDataJPAContext = this.getODataJPAContext();
try {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean(ENTITY_MANAGER_FACTORY_ID);
oDataJPAContext.setEntityManagerFactory(emf);
oDataJPAContext.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
return oDataJPAContext;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
...
My entity is quite simple ...
#Entity
public class User {
#Id
private String id;
#Basic
private String firstName;
#Basic
private String lastName;
....
Olingo is doing its job perfectly and it helps me with the generation of all the endpoints around CRUD operations for my entity.
My question is : how can I "inhibit" some of them? Let's say for example that I don't want to enable the delete my entity.
I could try to use a Filter - but this seems a bit harsh. Are there any other, better ways to solve my problem?
Thanks for the help.
As you have said, you could use a filter, but then you are really coupled with the URI schema used by Olingo. Also, things will become complicated when you have multiple, related entity sets (because you could navigate from one to the other, making the URIs more complex).
There are two things that you can do, depending on what you want to achieve:
If you want to have a fined grained control on what operations are allowed or not, you can create a wrapper for the ODataSingleProcesor and throw ODataExceptions where you want to disallow an operation. You can either always throw exceptions (i.e. completely disabling an operation type) or you can use the URI info parameters to obtain the target entity set and decide if you should throw an exception or call the standard single processor. I have used this approach to create a read-only OData service here (basically, I just created a ODAtaSingleProcessor which delegates some calls to the standard one + overridden a method in the service factory to wrap the standard single processor in my wrapper).
If you want to completely un-expose / ignore a given entity or some properties, then you can use a JPA-EDM mapping model end exclude the desired components. You can find an example of such a mapping here: github. The mapping model is just an XML file which maps the JPA entities / properties to EDM entity type / properties. In order for olingo to pick it up, you can pass the name of the file to the setJPAEdmMappingModel method of the ODataJPAContext in your initialize method.

My spring mvc session was changed implicitly after called a service method?

I'm using spring 3.2.5 via annotations and got some issue dealing with session.
My controller class is like this:
#Controller
public class WebController {
#Autowired
private IElementService elementService;
...
//in this method I set the "elementList" in session explicitly
#RequestMapping("/elementSearch.do")
public String elementSearch(
#RequestParam("keyword") String keyword,
HttpSession session){
List<Element> elementList= elementService.searchElement(keyword);
session.setAttribute("elementList", elementList);
return "searchResult";
}
//here I got my problem
#RequestMapping(value="/anotherMethod.do", produces="text/html; charset=utf-8")
#ResponseBody
public String anotherMethod(
...
//I called my service method here like
Element e = elementService.searchElement("something").get(0);
...
}
And I have a ElementServiceImpl class like this:
#Service
public class ElementServiceImpl implements IElementService {
#Autowired
private IBaseDAO baseDao;
#Override
public List<Metadata> searchElement(String keyword) {
List<Metadata> re = baseDao.searchElement(keyword);
return re;
}
}
And I have a BaseDAOImpl class implemented IBaseDAO and annonated with #Repository:
#Repository
public class BaseDAOImpl implements IBaseDAO {
...
}
Here is the problem, when I visit ".../anotherMethod.do", which will call the anotherMethod up there, my "elementList" in session was changed!
Then I looked into the anotherMethod() and found everytime
Element e = elementService.searchElement("something").get(0);
was called, my elementList was change to the new result returned by searchElement method(which returns a List).
But I didn't set session in that method, and I'm not using #SessionAttributes, so I don't understand how could my session attribute changed after calling a service method?
This problem is torturing me right now so any advise would be a great help, thanks!
update: I tried to print all my session attributes around that method call like this:
StringBuilder ss1 = new StringBuilder("-------------current session-------------\n");
session.setAttribute("test1", "test value 1");
log.info("sessionTest - key:test1 value:" + session.getAttribute("test"));
Enumeration<String> attrs1 = session.getAttributeNames();
while(attrs1.hasMoreElements()){
String key = attrs1.nextElement();
ss1.append(key).append(":").append(session.getAttribute(key)).append("\n");
}
log.info(ss1);
But I didn't see whether the "elementList" or the test value which I added just before print. And I do can get some value by
List<Element> elementList = (List<Element>) session.getAttribute("elementList");
and the elementList I get changed after calling service method, just like I posted before. Where my elementList stored? not in the session?
My goal is to show the elementList to the users in a table, and let them pick one of them, then I get the row number of the table and take it as a index of the elemntList, so I'll know which elemnt object the user picked. Are there any better way to do this so I can get rid of that problem?
Thanks again.

Resources