Reputation: 120861
I want to implement some functionality to obtain the current logged in User (the JPA entity that represent the person that is current logged in). But instead of doing it in the classic way
CurrentUserService.getCurrentUser(){load the user from db by login})
I want to do it in a more CDI way.
I want to have a CurrentUserFactory
, that provides the current user as an request scoped bean available with an custom qualifier (current bean).
Qualifier:
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface CurrentUser {
}
Factory:
@Component
public class CurrentUserFactory {
@CurrentUser
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public User currentUser() throws IllegalStateException {
return
<DoWhatIMean>
User load form DB by:
SecurityContextHolder.getContext().getAuthentication().getName()
</dwim>
}
Usage:
@Controller
@RequestMapping(…)
public AccountController() {
@Resource
@CurrentUser
private User currentUser;
@RequestMapping(params=”my”)
public String myAccount () {
return “redirect:/account/”+currentUser.id;
}
}
That works fine.
But it fails (with not realy usefull exceptions) if I use the current user for direct JPA (Hibernate) stuff like searching for an entity by reference to currentUser or storing an entity with reference to currentUser. -- (This this fails is clear.)
The reason is that spring inject an CGI proxy in the currentUser. This works fine as long as one uses only properties of this proxy, but fails if one starts to use the reference itself.
I know I am working at the edge of the specification and I accept if my idea does not work. But I want to ask you I have an idea to get it working. So that the developer can use this currentUser object like a normal JPA loaded Entity.
What I want at least is this very easy usage of current user only:
@Resouce
@CurrenctUser
User currentUser;
and that is it.
May someone has an idea to get it working, for example an AspectJ trick (I am using compile time AspectJ)?
Anyway: as I told before, the Exceptions are not my problem, I understand them because they are only indicators of the described problem.
Exception when I try to load by currentUser (Select b From Bookmark b Where user=?
)
java.lang.IllegalStateException
:org.hibernate.TransientObjectException
: object references an unsaved transient instance - save the transient instance before flushing:domain.User
Exception when I try to save a bookmark (Bookmark has a field user)
could not insert: [
domain.Bookmark
]; SQL [insert into bookmark (businessId, reference_Folder_fk, title, user_fk) values (?, ?, ?, ?)
]; constraint [null
]; nested exception isorg.hibernate.exception.ConstraintViolationException
: could not insert: [domain.Bookmark
]
Upvotes: 0
Views: 2280
Reputation: 781
The Spring proxy is created by ScopedProxyFactoryBean. Its documentation says the proxy implements 'ScopedObject', which lets you access the original bean.
@Resource
@CurrentUser
private User currentUser;
public static <T> T unwrapScopedProxy(T maybeProxy) {
if (maybeProxy instanceof ScopedObject) {
return (T) ((ScopedObject) maybeProxy).getTargetObject();
} else {
return maybeProxy;
}
}
...
query.setParameter("user", unwrapScopedProxy(currentUser));
Upvotes: 1