I have followed a tutorial on dynamic datasource routing tutorial in Spring. For that I have to extend AbstractRoutingDataSource to tell spring which datasource to get, so I do:
public class CustomRouter extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return CustomerContextHolder.getCustomerType();
}
}
Everything goes fine till I find the class responsible for keeping the value of the customerType (it should be the same during the whole session):
public class CustomerContextHolder {
private static final ThreadLocal<Integer> contextHolder = new ThreadLocal<Integer>();
public static void setCustomerType(Integer customerType) {
contextHolder.set(customerType);
}
public static Integer getCustomerType() {
return (Integer) contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
This creates a thread-bound variable customerType, but I have a web application with spring and JSF I don't think with threads but with sessions. So I set it in the login page with thread A (View), but then thread B (Hibernate) request the value to know what datasource to use, it is null indeed, because it has a new value for this thread.
Is there any way to do it Session-bounded instead of Thread-bounded?
Things I have tried so far:
Inject the CustomRouter in the view to set it in the session: Not working, it causes a cycle in dependecies
Replace the ThreadLocal with an Integer: Not working, the value is always set by the last user logged in
Is FacesContext.getCurrentInstance() working? If so then you may try with this:
public class CustomerContextHolder {
private static HttpSession getCurrentSession(){
HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance()
.getExternalContext().getRequest();
return request.getSession();
}
public static void setCustomerType(Integer customerType) {
CustomerContextHolder.getCurrentSession().setAttribute("userType", customerType);
}
public static Integer getCustomerType() {
return (Integer) CustomerContextHolder.getCurrentSession().getAttribute("userType");
}
public static void clearCustomerType() {
contextHolder.remove(); // You may want to remove the attribute in session, dunno
}
}
Related
I am working within an environment that changes credentials every several minutes. In order for beans that implement clients who depend on these credentials to work, the beans need to be refreshed. I decided that a good approach for that would be implementing a custom scope for it.
After looking around a bit on the documentation I found that the main method for a scope to be implemented is the get method:
public class CyberArkScope implements Scope {
private Map<String, Pair<LocalDateTime, Object>> scopedObjects = new ConcurrentHashMap<>();
private Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();
private Integer scopeRefresh;
public CyberArkScope(Integer scopeRefresh) {
this.scopeRefresh = scopeRefresh;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
.isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
}
return scopedObjects.get(name).getValue();
}
#Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable runnable) {
destructionCallbacks.put(name, runnable);
}
#Override
public Object resolveContextualObject(String name) {
return null;
}
#Override
public String getConversationId() {
return "CyberArk";
}
}
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk")
public String dateString(){
return LocalDateTime.now().toString();
}
}
#RestController
public class HelloWorld {
#Autowired
private String dateString;
#RequestMapping("/")
public String index() {
return dateString;
}
}
When I debug this implemetation with a simple String scope autowired in a controller I see that the get method is only called once in the startup and never again. So this means that the bean is never again refreshed. Is there something wrong in this behaviour or is that how the get method is supposed to work?
It seems you need to also define the proxyMode which injects an AOP proxy instead of a static reference to a string. Note that the bean class cant be final. This solved it:
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk", proxyMode=ScopedProxyMode.TARGET_CLASS)
public NonFinalString dateString(){
return new NonFinalString(LocalDateTime.now());
}
}
I have a Java Spring Service with a RestController that calls an async method:
#RestController
public class SomeController {
#Autowired
//this is the service that contains the async-method
OtherService otherService;
#GetMapping
public void someFunctionWithinTheMainRequestThread() {
otherService.asyncMethod(RequestContextHolder.getRequestAttributes());
}
}
That async method needs to use the RequestContextAttributes because it is building Links with linkTo(...). The problem is that no matter how I pass the RequestAttributes to the method, I always get the error
java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
This is the annotation on the async method:
public class OtherService {
#Async
#Transactional(readOnly = true)
public void asyncMethod(RequestAttributes context) {
RequestContextHolder.setRequestAttributes(context);
//doing a lot of stuff that takes a while
linkTo(methodOn(...)) //-> here the error occurs
}
What I tried:
Passing RequestAttributes manually as a Parameter (as seen in the code-snippets above)
Using the context-aware-pool executor described in this answer: How to enable request scope in async task executor - which basically seems to do the same as if I pass the context as a variable only that is is configured globally
Updating the servlet config and setting ThreadContextInheritable to true
Assigning the RequestAttributes to a final variable to try to get a copy of the original object which is marked as inactive by the main thread
No matter what I do, the request always seems to finish before my async method and I apparently never have a deep copy of the Attributes so they always get marked as inactive by the main thread before the async method is finished and then I can't use them anymore -> at least that is my understanding of the error.
I just want to be able to get the requestAttributes needed for the linkTo method in my async method even after the main thread finished the request, can someone point me in the right direction?
I found a solution that does work and removes the error. Since I don't think this is really clean I am hoping for more answers but in case it helps someone:
First I added this class. It creates a custom and very simple RequestAttributes-Implementation that enables us to keep the Attributes active for longer than they normally would be:
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class AsyncRequestScopeAttr extends ServletRequestAttributes {
private Map<String, Object> requestAttributeMap = new HashMap<>();
public AsyncRequestScopeAttr(HttpServletRequest request) {
super(request);
}
#Override
public void requestCompleted() {
//keep the request active, normally here this.requestActive would be set to false -> we do that in the completeRequest()-method which is manually called after the async method is done
}
/**
* This method should be called after your async method is finished. Normally it is called when the
* request completes but since our async method can run longer we call it manually afterwards
*/
public void completeRequest() {
super.requestCompleted();
}
#Override
public Object getAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.get(name);
}
return null;
}
#Override
public void setAttribute(String name, Object value, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST){
this.requestAttributeMap.put(name, value);
}
}
#Override
public void removeAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
this.requestAttributeMap.remove(name);
}
}
#Override
public String[] getAttributeNames(int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.keySet().toArray(new String[0]);
}
return new String[0];
}
#Override
public void registerDestructionCallback(String name, Runnable callback, int scope) {
// Not Supported
}
#Override
public Object resolveReference(String key) {
// Not supported
return null;
}
#Override
public String getSessionId() {
return null;
}
#Override
public Object getSessionMutex() {
return null;
}
#Override
protected void updateAccessedSessionAttributes() {
}
}
Then in the RestController before the async method is called:
#Autowired
//this is the service that contains the async-method
OtherService otherService;
public void someFunctionWithinTheMainRequestThread(){
otherService.asyncMethod(getIndependentRequestAttributesForAsync());
}
private RequestAttributes getIndependentRequestAttributesForAsync(){
RequestAttributes requestAttributes = new AsyncRequestScopeAttr(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest());
for (String attributeName : RequestContextHolder.getRequestAttributes().getAttributeNames(RequestAttributes.SCOPE_REQUEST)) {
RequestContextHolder.getRequestAttributes().setAttribute(attributeName, RequestContextHolder.getRequestAttributes().getAttribute(attributeName, RequestAttributes.SCOPE_REQUEST), RequestAttributes.SCOPE_REQUEST);
}
return requestAttributes;
}
And then in the async function:
public class OtherService {
#Async
#Transactional(readOnly=true)
public void asyncMethod(RequestAttributes context) {
//set the RequestAttributes for this thread
RequestContextHolder.setRequestAttributes(context);
// do your thing .... linkTo() etc.
//cleanup
((AsyncRequestScopeAttr)context).completeRequest();
RequestContextHolder.resetRequestAttributes();
}
}
again a small problem by understanding "how tapestry works".
I've got a Tapestry component (in this case a value encoder):
public class EditionEncoder implements ValueEncoder<Edition>, ValueEncoderFactory<Edition> {
#Inject
private IEditionManager editionDao;
public EditionEncoder(IEditionManager editionDao) {
this.editionManager = editionDao;
}
#Override
public String toClient(Edition value) {
if(value == null) {
return "";
}
return value.getName();
}
#Override
public Edition toValue(String clientValue) {
if(clientValue.equals("")) {
return null;
}
return editionManager.getEditionByName(clientValue);
}
#Override
public ValueEncoder<Edition> create(Class<Edition> type) {
return this;
}
}
Injecting the the Manager is not working, because the Encoder is created within a page like that:
public void create() {
editionEncoder = new EditionEncoder();
}
casued by this, i'm forced to use this ugly solution:
#Inject
private IEditionManager editionmanager;
editionEncoder = new EditionEncoder(editionManager);
Is there a better way to inject components during runtime or is there a better solution in general for it?
Thanks for your help in advance,
As soon as you use "new" then tapestry-ioc is not involved in object creation and can't inject. You should inject everything and never use "new" for singleton services. This is true for all ioc containers, not just tapestry-ioc.
Also if you put #Inject on a field then you don't also need a constructor to set it. Do one or the other, never both.
You should do something like this:
public class MyAppModule {
public void bind(ServiceBinder binder) {
binder.bind(EditionEncoder.class);
}
}
Then in your page/component/service
#Inject EditionEncoder editionEncoder;
If you wanted to put your own instantiated objects in there you can do
public class MyServiceModule {
public void bind(ServiceBinder binder) {
binder.bind(Service1.class, Service1Impl.class);
binder.bind(Service2.class, Service2Impl.class);
}
public SomeService buildSomeService(Service1 service1, Service2 service2, #AutoBuild Service3Impl service3) {
Date someDate = new Date();
return new SomeServiceImpl(service1, service2, service3, someDate);
}
}
I am newbie in Spring MVC and I have session scope component bean
#Component
#Scope(value="session",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class NotificationService {
static int unreadMessageCoutner;
static ArrayList<WebSocketNotificationDTO> unreadMessagesDTO;
#PostConstruct
public void initBean(){
this.unreadMessagesDTO = new ArrayList<WebSocketNotificationDTO>();
}
#PreDestroy
public void destroyBean(){
this.unreadMessagesDTO.clear();
this.unreadMessagesDTO=null;
}
#Override
public void addNotification(WebSocketNotificationDTO notification){
this.unreadMessagesDTO.add(notification);
}
#Override
public void incrementCounter(){
this.unreadMessageCoutner++;
}
#Override
public int getUnreadMessageCoutner(){
return this.unreadMessageCoutner;
}
#Override
public ArrayList<WebSocketNotificationDTO> getUnreadMessages(){
return this.unreadMessagesDTO;
}
#Override
public void setCounter(int counter){
//To do
}
}
I have update the attributes of this bean single time and call these method of this bean by #autowired and now I want to get the updated value of these attribute when I call get method.
Actually now getting NULL value whenever I have autowired this bean in different #controller.
Is this any way to get the single updated value in SPRING ?
Thanks in Advance
Note: Every user session have different value.
I have an action URL after clicking a hyper link like so
/SocialStupendous/GetProfile.action?slno=3&slno=3
In my execute method of ActionClass I have the following code
public String execute() {
int urislno=Integer.parseInt(getServletRequest().getParameter("slno"));
System.out.println(urislno);
bean.setUslno(urislno);
}
I am getting NullPointerException when I am performing bean.setuslno(urislno). Even though urislno is printed properly as 3.
ProfileBean class:
public class ProfileBean {
private int uslno;
public int getUslno() {
return uslno;
}
public void setUslno(int uslno) {
this.uslno = uslno;
}
}
Why is this happening?
The bean is not initialized. You should initialize it somehow in the action
private ProfileBean bean = new ProfileBean();
//and add getter ans setter
the better approach, however is let the container to do it for you. You just need to create a bean configuration in the struts.xml
<bean class="com.yourpackagename.ProfileBean" scope="default"/>
then you would have
private ProfileBean bean;
#Inject
public void setProfileBean(ProfileBean bean) {
this.bean = bean;
}
and you don't need to parse request for parameters, this is already done by the params interceptor which is a part of defaultStack that your action should run. You should create properties in your action to hold parameter values.
private Integer slno;
public Integer getSlno() {
return slno;
}
public void setSlno(Integer uslno) {
this.slno = slno;
}
and the action will look like
public String execute() {
if (slno != null) {
System.out.println(slno)
bean.setUslno(slno);
}
......
return SUCCESS;
}