I am trying to store my user Object as a singleton with Dagger 2.
#Provides
#Named("me")
#Singleton
User provideUser(PrefsUtil prefsUtil, UserDao userDao) {
int id = prefsUtil.getFromPrefs("me", 0);
if (id == 0){
return new User();
}
try {
return userDao.queryForId(id);
} catch (SQLException e) {
return new User();
}
}
It works fine and injects my classes with User object.
However, after logging in and fetching the user from server and storing it in the place the above method queries it from, it will not take effect because it is a singleton. It will provide me with the null user object. In order for it to take effect you have to quit application and reopen it...
The question is how to update/reinitialize the user object annotated with #Name("me") after the actual data is changed so it injects my other classes with the current user object?
I'm not going to answer your direct question, but give you an advice how to properly implement the functionality that you need.
You are basically trying to implement some kind of UserManager functionality. But instead of encapsulating this logic in a dedicated class, you attempt to delegate the user management responsibilities to DI framework.
This is an abuse of DI framework and very sloppy path to go.
What you need is just this:
#Provides
#Singleton
UserManager provideUserManager(PrefsUtil prefsUtil, UserDao userDao) {
return new UserManager(prefUtils, userDao);
}
And expose the required funcitonality in UserManager:
public class UserManager {
private final PrefsUtil mPrefsUtil;
private final UserDao mUserDao;
public UserManager(PrefsUtil prefsUtil, UserDao userDao) {
mPrefsUtil = prefsUtil;
mUserDao = userDao;
}
public User getCurrentUser() {
int id = mPrefsUtil.getFromPrefs("me", 0);
if (id == 0){
return new User();
}
try {
return mUserDao.queryForId(id);
} catch (SQLException e) {
return new User();
}
}
}
You can see this and this answers in order to get some additional context about DI framework abuse.
You might also want to read this post: Dependency Injection in Android.
Then it no longer may be annotated with Singleton. You have to create your custom Scope.
Then you take responsibility for the object annotated with your custom scope. As soon as your User has been updated you are getting rid of the previous component that provided User object, i.e. nulling it out. Then you are creating a new component and the next time you ask the component to fetch you the User it will create a new one.
Be aware, that any other provider method in the module, that was annotated with your custom scope, will also return newly created object.
Here's a blog post describing how to do that.
Related
Spring's Security "hasPermission" method has an implementation, which (as I get) is intended for passing class name (targetType) and Object Id (Serializable).
So could you please explain (at least in general) how to do this implementation right?
I've searched for example of passing object ID and found no any (even at Spring's doc).
In my situation I want to check for User's DELETE permission on some of my classes (for instance, "Goal"). All of these classes has universal methods and fields, so I can have universal logic for checking permission inside a PermissionEvaluator.
For doing this I'm intended to pass an Object's ID and Object's class name into PermissionEvaluator and do the check here like this:
#PreAuthorize("hasPermission(#id, 'Goal','DELETE')")
It sounds pretty good till it not comes to the implementation, because I don't really understand how can I get Object's instance by Class name and Id inside Permission evaluator.
#Component
public class CustomPermissionEvaluator implements PermissionEvaluator
#Override
public boolean hasPermission(Authentication authentication, Serializable serializable, String targetType,
Object permission) {
Yes, I can instantiate object by Class.forName(targetType), but how can I get it's instance by Id (serializable) from appropriate Repository then? (I have different repository for every object).
#Autowiring all of my 30 repositories would be the madness.
Implemented my service, which takes Object ID and Object Type and then sends back Object, which I can later unbox. I used dynamic HQL, so no need in 30+ JPA repositories autowiring (my bad, I missed this possibility at the beginning).
#PersistenceContext
EntityManager entityManager;
static String entityClassPath="com.platform.entity.";
public Object getEntity(String className, Long id) {
String classToQuery = capitalize(className);
/* Check if Entity class exists to decide whether to query DB or not */
try {
Class cls = Class.forName(entityClassPath + className);
} catch (Exception e) {
return null;
}
/* Query DB if Entity class exist */
Query query;
try {
query = entityManager.createQuery("SELECT Q FROM " + classToQuery + " Q WHERE Q.id=?1");
query.setParameter(1, id);
return query.getSingleResult();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// This is my LoginController
public String login() throws InvoiceException{
//HttpSession session ;
String email = this.getEmail();
String password = this.getPassword();
if(loginService.authenticateLogin(email, password) == true){
System.out.println("Login Successful");
customer = loginService.getCustomerBean(email);
//Creating a new Session
/*session = (HttpSession) sessionManager.getSession("CustomerInfo");
session.setAttribute("CustomerId",customer.getCustomerId());
session.setAttribute("email", customer.getEmail());
session.setAttribute("CustomerName", customer.getFirstName());
sessionManager.setSession("CustomerInfo", session);*/
sessionManager.setSession("CustomerId", customer.getCustomerId());
sessionManager.setSession("email", customer.getEmail());
sessionManager.setSession("CustomerName", customer.getFirstName());
return "ProfilePage";
}
else{
setErrorMessage("Bad Username/Password");
return "LoginOrRegister";
}
}
/*This is my Profile page Controller's contructor. profileService autowired at beginning during instance variable declaration part of the class.
sessionManager is an instance of class SessionManager which contains methods for get and set sessions for session Tracking in JSF.
i want to display the profile page of the user using with all his/her values that are stored in DB. But when spring is instantiating beans to inject, all these values will be set to null initially isnt it? how do i set these inital values ? also the service class isnt gettng instantiated which is autowired.
public ProfilePageController(){
sessionManager = new SessionManager();
customerId = (Integer) sessionManager.getSession("CustomerId");
email = (String) sessionManager.getSession("email");
firstName = (String) sessionManager.getSession("CustomerName");
try {
customer = profileService.getCustomerBean(customerId);
} catch (InvoiceException e) {
e.printStackTrace();
}
this.setFirstName(customer.getFirstName());
this.setLastName(customer.getLastName());
this.setEmail(customer.getEmail());
this.setPassword(customer.getPassword());
this.setCity(customer.getCity());
this.setState(customer.getState());
this.setCountry(customer.getCountry());
this.setPhoneNumber(customer.getPhoneNumber());
this.setGender(customer.getGender());
this.setZipCode(customer.getZipCode());
this.setCustomerId(customer.getCustomerId());
}
That can't possibly work. Put yourself in Spring's shoes. You need to create a ProfilePageController and autowire some of its fields. How can you do that?
Well, in order to set a field in an object, you need that object, right. So you need to first create the object. How can you create an object? By calling its constructor, right?
But your constructor tries to use the field, and that field will only be autowired after the constructor has been called. So you get a NullPointerException.
There are several solutions (that can be combined)
use constructor injection rather than field injection. This is a good idea in general: it makes testing easier, and makes the dependencies clearer
do your initialization work inside a method annotated with #PostConstruct.
I have three different classes:
Managed bean (singleton scope)
Managed bean (session scope)
Spring #Controller
I read few posts here about synchronization, but I still don't understand how it should be and how it works.
Short examples:
1) Managed bean (singleton scope).
Here all class fields should be the same for all users. All user work with one instance of this object or with his copies(???).
public class CategoryService implements Serializable {
private CategoryDao categoryDao;
private TreeNode root; //should be the same for all users
private List<String> categories = new ArrayList<String>();//should be the same for all users
private List<CategoryEntity> mainCategories = new ArrayList<CategoryEntity>();
//should be the same for all users
public void initCategories() {
//get categories from database
}
public List<CategoryEntity> getMainCategories() {
return mainCategories;
}}
2) Managed bean (session scope)
In this case, every user have his own instance of object.
When user trying to delete category he should check are another users which trying to delete the same category, so we need to use synchronized block???
public class CategoryServiceSession implements Serializable {
private CategoryDao categoryDao;
private CategoryService categoryService;
private TreeNode selectedNode;
public TreeNode getSelectedNode() {
return selectedNode;
}
public void setSelectedNode(TreeNode selectedNode) {
this.selectedNode = selectedNode;
}
public void deleteCategory() {
CategoryEntity current = (CategoryEntity) selectedNode.getData();
synchronized (this) {
//configure tree
selectedNode = null;
categoryDao.delete(current);
}
categoryService.initCategories();
}}
3) Spring #Controller
Here all user may have an instance (or each user have his own instance???). But when some admin try to change parameter of some user he should check is another admin trying to do the same operation??
#Controller
#RequestMapping("/rest")
public class UserResource {
#Autowired
private UserDao userDao;
#RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public #ResponseBody UserEntity changeBannedStatus(#PathVariable Long id) {
UserEntity user = userDao.findById(id);
synchronized (id) {
user.setBanned(!user.getBanned());
userDao.update(user);
}
return user;
}
}
So, how it should to be?
Sorry for my English.
In the code that you've posted -- nothing in particular needs to be synchronised, and the synchronised blocks you've defined won't protect you from anything. Your controller scope is singleton by default.
If your singletons change shared objects ( mostly just their fields) then you should likely flag the whole method as synchronised.
Method level variables and final parameters will likely never need synchronization ( at least in the programming model you seem to be using ) so don't worry about it.
The session object is guarded by serialisation, mostly, but you can still have data races if your user has concurrent requests -- you'll have to imagine creative ways to deal with this.
You may/will have concurrency issues in the database ( multiple users trying to delete or modify a database row concurrently ) but this should be handled by a pessimistic or optimistic locking and transaction policy in your DAO.
Luck.
Generally speaking, using synchronized statements in your code reduces scalability. If your ever try to use multiple server instances, your synchronized will most likely be useless. Transaction semantics (using either optimistic or pessimistic locking) should be enough to ensure that your object remains consistent. So in 2 und 3 you don't need that.
As for shared variables in CategoryService it may be possible to synchronize it, but your categories seem to be some kind of cache. If this is the case, you might try to use a cache of your persistence provider (e.g. in Hibernate a second-level cache or query cache) or of your database.
Also calling categoryService.initCategories() in deleteCategory() probably means you are reloading the whole list, which is not a good idea, especially if you have many categories.
Guice users! I have a situation here and I could find a workaround, but I'm not satisfied with my solution. It's very similar to Using the provider from two different scopes, but the answer there doesn't fit my situation.
I have a class like this, which I inject in a lot of places:
MyBusinessClass {
#Inject
MyBusinessClass(#AuthenticatedUser User user) {};
}
Up to some moment in the past, I just got the #AuthenticatedUser User from the web session, so I had:
bind(User.class).annotatedWith(AuthenticatedUser.class).toProvider(new AuthenticatedUserProvider());
...
public static class AuthenticatedUserProvider implements Provider<User> {
#Inject
Provider<Session> session;
public User get() {
return SessionUtil.getUserFromSession(session.get());
}
}
The problem:
That worked great till I needed to use the same MyBusinessClass inside a different Guice scope (and also outside the request scope). I created a JobScope, very similiar to the scope example in Guice docs, created a kind of JobSession, binded it to the JobScope, and put the #AuthenticatedUser User instance I want injected when MyBusinessClass is used inside the JobSession.
That's where I'm not proud of what I did.. I "improved" my provider to try to provide the #AuthenticatedUser User for all scopes, and I ended up with this ugly provider:
public static class AuthenticatedUserProvider implements Provider<User> {
#com.google.inject.Inject(optional=true)
Provider<Session> session;
#com.google.inject.Inject(optional=true)
Provider<JobSession> jobSession;
#Override
public User get() {
try {
return SessionUtil.getUserFromSession(session.get());
} catch (Exception e) {
try {
return SessionUtil.getUserFromJobSession(jobSession.get());
} catch (Exception ee) {
throw new IllegalStateException("Current scope doesn't have a auth user!");
}
}
}
}
The provider does a try-an-error approach to find which session (web session or job session) is available and return the user for the first one it is able to get. It works because of the #com.google.inject.Inject(optional=true) and also because the scopes are mutually exclusive.
Is there a better way to achieve this? I just want to have MyBusinessClass injected with #AuthenticatedUser User for any scope it is used transparently, and let Guice modules/providers find the right place to get the satisfying instance.
As i understand spring mvc controllers are thread safe by default (like servlets). But I just want to know any private helper methods inside the controllers are thread safe ?
I have two mapping in the controller class eg: /test and test/success. Every time user invokes this url I want to check the user status and activation time in the database using a service method ( two different calls ). So I have decided to create a one private helper method to check the status.
So could anyone know that my private method is thread safe ?
All request are handled by one instance of your controller (singleton because it's a spring managed bean). So you need to make sure to not store any state (in a field) related to one request.
So:
#Controller
#RequestMapping("/foo")
public class Foo {
#Autowired
private Something something;
#RequestMapping("/list")
public String foo() {
something.someMethod();
bar();
return "view"
}
private void bar() {
// something
}
}
is OK, but:
#Controller
#RequestMapping("/foo")
public class Foo {
private User theUser; // problem is ALL request share this field
#RequestMapping("/foo/{userId}")
public String foo(#PathVariable final Integer userId) {
if (theUser.getId().equals(userId)) {
// something
} else {
theUser = ...
}
return "view"
}
}
is not.
NB: not tested (typed just here so it can even hurts your compiler)