How to log when jwt expired in spring boot - spring

I want to log user when logged-in and logged-out, and in also when jwt token expired. To do this requirement i call loginHistoryService.saveUserLogout() when ExpiredJwtException occurs, but the problem is that this method called more than one, because the when user opens the form, this form contains a lots of ajax requests, so each request cause to go to this filter and log user logout multiple times.
Is there a better way to implement this requirement?
public class JWTAuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if (!request.getRequestURI().contains("/signout")) {
try {
if (checkJWTToken(request)) {
Claims claims = validateToken(request);
if (claims.get(CLAIM_AUTHORITIES_KEY) != null) {
setUpSpringAuthentication(claims);
}
else {
SecurityContextHolder.clearContext();
}
}
else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
} catch (ExpiredJwtException e) {
// call log method here, but this method called more than one
loginHistoryService.saveUserLogout();
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("ExpiredJwt", "true");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your JWT token expired!");
} catch (UnsupportedJwtException | MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "forbidden!");
}
}
else {
chain.doFilter(request, response);
}
}
}
UPDATE 1
The purpose of loginHistoryService is store LoginHistory object with below declaration:
#Entity
#Table(name = "tbl_login_history")
public class LoginHistory extends BaseEntity<Long> {
#Column(name = "user_name", nullable = false)
private String userName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
#Column(name = "is_success", nullable = false)
private boolean success;
#Column(name = "is_login", nullable = false)
private boolean login;
#Column(name = "browser_type")
private BrowserType browserType;
#Column(name = "browser_name")
private String browserName;
#Column(name = "operating_system_type")
private OperatingSystemType operatingSystemType;
#Column(name = "operating_system_name")
private String operatingSystemName;
#Column(name = "login_failure_reason")
private LoginFailureReason loginFailureReason;
}
That audits user logins and logouts.
UPDATE 2
I changed LoginHistory and added jwtToken field to store jwt and check if a record exists, then do not insert. but the problem is that it the records inserted multiple time.
#Service
public class LoginHistoryService implements ILoginHistoryService {
#Autowired
private ILoginHistoryRepository loginHistoryRepository;
#Override
#Transactional
public void saveLogWhenExpiredUser(String token) {
if (!loginHistoryRepository.checkExpiredTokenExistence(token)) {
// save the log record
}
}
}
And this is the method that checks record existence:
#Override
public boolean checkExpiredTokenExistence(String token) {
String hql = " select cast((case when count(*) = 0 then 0 else 1 end) as boolean) " +
" from " + domainClass.getName() + " e " +
" where e.jwtToken = :jwtToken " +
" and e.logoutType = :logoutType " ;
Map<String, Object> params = new HashMap<>();
params.put("jwtToken", token.getBytes());
params.put("logoutType", LogOutType.Expired);
return super.find(hql, params);
}

because the when user opens the form, this form contains a lots of
assets and ajax requests, so each request cause to go to this filter
and log user logout multiple times
Your public assets must not be taken into account by your security filter. Kindly ignore security check on your public assets

Just add a constraint on uniqueness of jwtToken and logoutType:
#Entity
#Table(uniqueConstraints={
#UniqueConstraint(columnNames = {"jwtToken", "logoutType"})
}, name = "tbl_login_history")
public class LoginHistory extends BaseEntity<Long> {
...
}

Related

Get actual user details with spring boot

Actually I´m working in a forum project built with Spring boot, Mongodb and Vue.js.
When I´m trying to post a new comment and get the user datails with the SecurityContextHolder and cast it to my UsersDetailImpl who implements from the UserDetails class provided by Spring boot, it throw the following error: org.springframework.security.web.authentication.webauthenticationdetails cannot be cast to .... UserDetailsImpl
I don´t really know the reason of this error becasuse if I test it from Postman does not report an error.
UserDetailsImpl.java
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String email;
#JsonIgnore
private String password;
private Collection<? extends GrantedAuthority> authorities;
public UserDetailsImpl(String id, String username, String email, String password,
Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public static UserDetailsImpl build(User user) {
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList());
return new UserDetailsImpl(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getPassword(),
authorities);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public String getId() {
return id;
}
public String getEmail() {
return email;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id);
}
}
CommentController.java
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/comments")
public class CommentController {
#Autowired
CommentRepository commentRepository;
#Autowired
RoleRepository roleRepository;
#PostMapping("/ask")
public ResponseEntity<?> ask (#Valid #RequestBody AskRequest askRequest) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
HashSet<String> strRoles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toCollection(HashSet::new));
Set<Role> roles = new HashSet<>();
strRoles.forEach(role -> {
int cutPoint = role.indexOf("_");
role = role.substring(cutPoint + 1).toLowerCase();
findRole(roles, role, roleRepository);
});
User user = new User(userDetails.getUsername(), userDetails.getEmail(), roles);
ObjectId discussion_id = ObjectId.get();
String slug = new Slugify().slugify(askRequest.getTitle());
Comment comment = new Comment(discussion_id, askRequest.getTitle(),
askRequest.getText(),slug, "full_slug_test", Instant.now(),user);
String info = comment.getDiscussion_id().toString() + comment.getPosted() + comment.getTitle()
+ comment.getText() + comment.getAuthor().getUsername() + comment.getAuthor().getEmail()
+ comment.getAuthor().getId() + comment.getAuthor().getRoles();
commentRepository.save(comment);
return ResponseEntity.ok(new MessageResponse(info));
}
}
I´m new in all this technologies there may be serious errors. All the advices will be a great help to me because the project is academic.
If someone need more information just ask for it.
Thank you all :)
Change authentication.getDetails() to getAuthentication().getPrincipal()
You will have:
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
Finally I found the error and it was in the front-end side. I was sending de headers with the JWT in this way.
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/comments/';
class CommentsService {
ask(post){
return axios.post(API_URL + 'ask', {
title: post.title,
text: post.text,
headers: authHeader()
});
}
}
export default new CommentsService();
and it is totally wrong so I found the manner to do it.
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/comments/';
class CommentsService {
ask(post){
return axios.post(API_URL + 'ask', {
title: post.title,
text: post.text
},{headers: authHeader()});
}
}
export default new CommentsService();
I also add the code to mount the headers.
export default function authHeader() {
let user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}

Spring Rest Api Design For Following a User in Twitter Clone Appliction?

I Want to Design a Demo Twitter Clone Application where user can follow any other user . however i am doubting my rest api design . please suggest me am i right .
Can I pass followerId in url rather than passing it as requestbody as we already know followerId in Advance and server does not create followerId here ?
and if better option could be there like put/patch or any rest api design ?
Please suggest me better design if possible
Here JwtUser is Authenticated User
public class FollowerDto {
private Long followerId;
private boolean following;
public FollowerDto() {
}
public FollowerDto(Long followerId, boolean following) {
this.followerId = followerId;
this.following = following;
}
public boolean getFollowing() {
return following;
}
public void setFollowing(boolean following) {
this.following = following;
}
public Long getFollowerId() {
return followerId;
}
public void setFollowerId(Long followerId) {
this.followerId = followerId;
}
}
#PostMapping("/follower")
#ResponseStatus(HttpStatus.CREATED)
public StatusDto addFollower(#RequestBody #Valid final FollowerDto
followerDto, #CurrentUser final JwtUser user, final
HttpServletResponse response) {
RestPreconditions.checkRequestElementNotNull(followerDto);
RestPreconditions.checkArgumentCondition(followerDto.getFollowing());
return userService.addFollower(user, followerDto.getFollowerId(),
response);
}
// Service Layer
#Override
public StatusDto addFollower(final JwtUser jwtUser, final Long followerId, final HttpServletResponse response) {
final User follower = userRepository.findById(followerId).orElse(null);
ServicePreconditions.checkEntityExists(follower, "Follower does not exist with id " + followerId);
final User currentUser = userRepository.findByEmail(jwtUser.getEmail());
if (currentUser != null) {
ServicePreconditions.checkOKArgument(!currentUser.equals(follower));
final Set<User> existingFollowers = currentUser.getFollowers();
if (existingFollowers != null) {
existingFollowers.add(follower);
} else {
currentUser.setFollowers(Sets.<User>newHashSet(follower));
}
userRepository.save(currentUser);
final URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/{idOfNewResource}").buildAndExpand(follower.getId()).toUri();
response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString());
return new StatusDto("Follower Added Successfully to user having email " + jwtUser.getEmail());
}
return new StatusDto("Follower is not Added to user with email " + jwtUser.getEmail());
}

Update User's first name and last name in principal

I am updating user's information like first name and last name and I am getting first name and last name in all the pages for welcome message.
I have two controllers one for ajax request mapping and the other for normal request mapping.
Normal request mapping controller have this method. In this controller all page navigation is present and some request mapping which are not ajax calls
private String getPrincipalDisplay() {
GreenBusUser user = null;
String userName = "";
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
user = (GreenBusUser) principal;
userName = user.getFirstName() + " " + user.getLastName();
} else {
userName = "";
}
return userName;
}
This is how I am getting the username on every page by return string of this function I am adding it in ModelMap object.
When I update user's information I am doing in ajax request mapping.
#RequestMapping(value = "/restify/updateUserData", method = RequestMethod.PUT, headers = "Accept=application/json")
public ServiceResponse forgotPassword(#RequestBody Object user)
{
//logger.debug("getting response");
return setDataPut("http://localhost:7020/forgotPassword",user);
}
user is an Object type which has json data. Now how do I retrieve data from object and update my first name and last name in principal.
This is my GreenBusUser class
public class GreenBusUser implements UserDetails
{
private static final long serialVersionUID = 1L;
private String username;
private String password;
private Collection<? extends GrantedAuthority> grantedAuthorities;
private String firstName;
private String lastName;
public GreenBusUser(String username,String password,Collection<? extends GrantedAuthority> authorities,String firstName, String lastName)
{
this.username = username;
this.password = password;
this.grantedAuthorities = authorities;
this.firstName=firstName;
this.lastName=lastName;
this.grantedAuthorities.stream().forEach(System.out::println);
}
public Collection<? extends GrantedAuthority> getAuthorities()
{
return grantedAuthorities;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getPassword()
{
return password;
}
public String getUsername()
{
return username;
}
public boolean isAccountNonExpired()
{
return true;
}
public boolean isAccountNonLocked()
{
return true;
}
public boolean isCredentialsNonExpired()
{
return true;
}
public boolean isEnabled()
{
return true;
}
}
UPDATE:::::
I have updated your code and applied some part of your answer into mine but still I ran into a problem
#RequestMapping(value="/updateUser",method=RequestMethod.GET)
public String updateUser(ModelMap model) {
UserInfo user = getUserObject();
GreenBusUser newGreenBususer = null;
List<User> list = new ArrayList<User>();
list = FetchDataService.fetchDataUser("http://localhost:8060/GetuserbyUserName?username=" + getPrincipal(), user.getUsername(), user.getPassword());
logger.debug("new user list ----->>>"+list.size());
User newuser=(User)list.get(0);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
SecurityContextHolder.getContext().getAuthentication().getPrincipal(), SecurityContextHolder.getContext().getAuthentication().getCredentials());
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
newGreenBususer=(GreenBusUser)principal;
logger.debug("newGreenBususerDetails---->>>"+newGreenBususer.toString());
newGreenBususer.setFirstName(newuser.getFirstName());
newGreenBususer.setLastName(newuser.getLastName());
if(newGreenBususer.getFirstName()!=null) {
logger.debug("got my first name");
}
if(newGreenBususer.getLastName()!=null) {
logger.debug("got my last name");
}
auth.setDetails(newGreenBususer);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
model.addAttribute("user", getPrincipalDisplay());
model.addAttribute("userData", list);
model.addAttribute("check", true);
return "GreenBus_updateProfile_User";
}
At first it sets the firstname and lastname to GreenBusUser and then there is setDetails method when I reload the page it says No user found when I am calling getUserObject() method at the top of this method.
private X2CUser getUserObject() {
X2CUser userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
userName = ((X2CUser) principal);
} else {
logger.info("No user found");
}
return userName;
}
If you are updating the password, then it will be good to logout the user and tell him to relogin.
Try this code .. It might help you.
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(user, pass);
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
I have finally resolved my problem though I have later added some code in my question part in UPDATE section.
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
newGreenBususer=(GreenBusUser)principal;
newGreenBususer.setFirstName(newuser.getFirstName());
newGreenBususer.setLastName(newuser.getLastName());
Yes that's all need to be done.
This part--->>
auth.setDetails(newGreenBususer);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
set new context making security pointing to null when I reload still not clear because I am setting the details before reload so its like I get new context but I have set the new user details.
Though I have finally resolved my problem but if anyone could shed some light why it was happening then I will accept his/her answer.
Thanks alot for your support. Keep Learning!

Spring JPA: Locking parent row when inserting one to many child record

We have two tables that have a one to many relationship. When we insert multiple records into the child table across multiple threads (more specifically across multiple REST web requests) we are running into lost update issues due to a race condition.
What we need to be able to do is have JPA recognize that the entity has been updated elsewhere prior to inserting the child record. I've tried using the #Version annotation approach but that doesn't seem to do the trick as the update/insert (I guess...) is happening on another table. I tried adding a version timestamp column on the parent table that is updated on every update but that didn't seem to do the trick either.
I think what I actually need to do is get a reference to the EntityManager directly so that I can issue a lock() command on the record prior to calling save(). I'm just too new to Spring to know if
A) that is indeed the correct approach,
B) if there is a better/easier way to do what we are trying to accomplish, and
C) how to actually do that.
Also, I am aware of the #OneToMany annotation but that didn't seem to do anything.
I've truncated the code below for brevity and I also created a trimmed down version of the code that demonstrates the problem and will hopefully make it easier to see what I am trying to do. In the test if you change the thread pool number to 1 you can see the test pass.
Engagement class:
#Entity
public class Engagement implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ElementCollection(fetch = EAGER)
private List<String> assignedUsers;
#Version
private Long version;
private LocalDateTime updatedOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion(){return version;}
public void setVersion(Long version){this.version = version;}
public LocalDateTime getUpdatedOn(){
return updatedOn;
}
public void setUpdatedOn(LocalDateTime updatedOn) {
this.updatedOn = updatedOn;
}
public List<String> getAssignedUsers() {
return assignedUsers;
}
public void setAssignedUsers(List<String> assignedUsers) {
this.assignedUsers = assignedUsers;
}
public Engagement() {
}
}
User class:
public final class User {
private final String name;
private final String email;
private final String userId;
private final List<Engagement> engagements;
#ConstructorProperties({"roles", "name", "email", "userId", "engagements"})
User(String name, String email, String userId, List<Engagement> engagements) {
this.name = name;
this.email = email;
this.userId = userId;
this.engagements = engagements;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public String getName() {
return this.name;
}
public String getEmail() {
return this.email;
}
public String getUserId() {
return this.userId;
}
public List<Engagement> getEngagements() {
return this.engagements;
}
public static final class UserBuilder {
private String name;
private String email;
private String userId;
private List<Engagement> engagements;
UserBuilder() {
}
public User.UserBuilder name(String name) {
this.name = name;
return this;
}
public User.UserBuilder email(String email) {
this.email = email;
return this;
}
public User.UserBuilder userId(String userId) {
this.userId = userId;
return this;
}
public User.UserBuilder engagements(List<Engagement> engagements) {
this.engagements = engagements;
return this;
}
public User build() {
return new User(this.name, this.email, this.userId, this.engagements);
}
public String toString() {
return "User.UserBuilder(name=" + this.name + ", email=" + this.email + ", userId=" + this.userId + ", engagements=" + this.engagements + ")";
}
}
}
Thread test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class EngagementTest {
#Mock
UsersAuthService usersService;
#Autowired
EngagementsRepository engagementsRepository;
UsersAuthService authService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
authService = new UsersAuthServiceImpl(usersService, engagementsRepository);
}
#Test
public void addingMultipleUsersAtOnceSucceeds() throws InterruptedException {
Long engagementId = 1L;
String userId1 = "user1";
String userId2 = "user2";
String userId3 = "user3";
String userId4 = "user4";
String userId5 = "user5";
String auth = "asdf";
User adminUser = User.builder()
.userId("adminUser")
.email("user#user.com")
.name("Admin User")
.build();
Engagement engagement = new Engagement();
engagement.setAssignedUsers(new ArrayList<>());
engagement.getAssignedUsers().add(adminUser.getUserId());
engagementsRepository.save(engagement);
ExecutorService executorService = Executors.newFixedThreadPool(5);//change this to 1 to see the test pass
List<Callable<Engagement>> callableList = Arrays.asList(
addUserThread(engagementId, userId1, auth, adminUser),
addUserThread(engagementId, userId2, auth, adminUser),
addUserThread(engagementId, userId3, auth, adminUser),
addUserThread(engagementId, userId4, auth, adminUser),
addUserThread(engagementId, userId5, auth, adminUser));
executorService.invokeAll(callableList);
Engagement after = engagementsRepository.findById(engagementId);
assertEquals(6, after.getAssignedUsers().size());
}
private Callable<Engagement> addUserThread(Long engagementId, String userId1, String auth, User adminUser) {
return () -> authService.addUserTo(engagementId, userId1, auth, adminUser);
}
}
What's happening here is that you submit the callbacks for execution but never actually wait for their completion before checking the result. You need to use the List<Future<Engagement>> to actually wait for the results to complete before proceeding.
Something like this would do the trick:
executorService.invokeAll(callableList).forEach(it -> {
try {
it.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
});
Note that this is not a proper way to deal with the exception case but it causes the code to wait for completion. If you have that in place you see the threads properly rejecting some of the updates with an ObjectOptimisticLockingFailureException:
java.util.concurrent.ExecutionException: org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.example.racecondition.engagement.Engagement] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.example.racecondition.engagement.Engagement#1]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:206)
at com.example.racecondition.EngagementTest.lambda$0(EngagementTest.java:68)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.example.racecondition.EngagementTest.addingMultipleUsersAtOnceSucceeds(EngagementTest.java:66)
What's weird about the test case beyond that is that UsersAuthServiceImpl carries an #Transactional but the test case manually instantiates that class, so that there's no transactional proxy in place already. This causes the calls to findById(…) and save(…) from within addToUser(…) to run in two transactions. Tweaking that doesn't change the output though.
I think what I actually need to do is get a reference to the EntityManager directly so that I can issue a lock() command on the record prior to calling save(). I'm just too new to Spring to know if
A) that is indeed the correct approach,
If I understand you correctly you want to basically force a version increment on an entity so that if multiple threads do that one fails.
You can indeed achieve that by locking the entity in question using LockModeType.PESSIMISTIC_FORCE_INCREMENT or LockModeType.OPTIMISTIC_FORCE_INCREMENT.
B) if there is a better/easier way to do what we are trying to accomplish, and
C) how to actually do that.
With Spring Data probably the best way to do that is using the #Lock annotation on the method you use to load the entity.

Cannot delete entity (JPA & Spring)

What ever I try, I cannot delete a user entity when I call delete() from my userService class. I get an exception java.lang.IllegalArgumentException: Entity must be managed to call remove: com.blackbox.genesis.entities.User#30168a, try merging the detached and try the remove again. I'm obviously doing something wrong - despite merging, but I can't see what. Everything else works fine - I can create and update user entities without any problem.
Regards
My entity class;
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = "EMAIL")})
public class User implements Serializable {
#Id
#Column(name="username", length=50)
private String username;
#OneToOne(cascade = {CascadeType.ALL})
private Password password;
private boolean enabled;
private int serial;
private String email;
#Version
private int version;
#ElementCollection(targetClass=Authority.class)
#CollectionTable(name="USER_AUTHORITY")
private List<Authority> authorities;
#OneToMany(mappedBy="user", fetch=FetchType.LAZY, cascade=CascadeType.ALL, ``orphanRemoval=true)
private Set<License> licenses;
private static final long serialVersionUID = 1L;
public User() {
super();
this.authorities = new ArrayList<Authority>();
}
.... getters/setters.
My DAO class;
#Repository
public class UserJpaController {
#PersistenceContext
EntityManager em;
protected static final Logger logger = Logger.getLogger("com.blackbox.genesisng.entities.UsersJpaController");
public void create(User user) throws PreexistingEntityException, Exception {
if (findUser(user.getUsername()) != null) {
throw new PreexistingEntityException("Users " + user + " already exists.");
}
em.persist(user);
em.flush();
}
public void edit(User user) throws NonexistentEntityException, Exception {
user = em.merge(user);
em.flush();
}
public void destroy(String id) throws NonexistentEntityException {
User user = em.find(User.class, id);
user = em.merge(user);
em.remove(user);
}
public List<User> findUserEntities() {
return findUserEntities(true, -1, -1);
}
public List<User> findUserEntities(int maxResults, int firstResult) {
return findUserEntities(false, maxResults, firstResult);
}
private List<User> findUserEntities(boolean all, int maxResults, int firstResult) {
Query q = em.createQuery("select object(o) from User as o");
if (!all) {
q.setMaxResults(maxResults);
q.setFirstResult(firstResult);
}
return q.getResultList();
}
public User findUser(String id) {
return em.find(User.class, id);
}
public int getUserCount() {
Query q = em.createQuery("select count(o) from User as o");
return ((Long) q.getSingleResult()).intValue();
}
public User findUserByEmail(String email) {
Query q = em.createQuery("select Object(o) from User as o where o.email = :email");
q.setParameter("email", email);
List list = q.getResultList();
if (list.size() == 0) {
return null;
}
return (User) list.get(0);
}
public boolean exists(String id) {
try {
em.getReference(User.class,id);
return true;
}
catch (EntityNotFoundException e) {
return false;
}
}
}
and finally, the relevant portion of my service class
#Service
public class UserService {
#Autowired
UserJpaController dao;
#Autowired
LicenseJpaController licenseDao;
#Transactional
public void delete(UserDTO userDTO) {
if (exists(userDTO.getUserName())){
try {
dao.destroy(userDTO.getUserName());
} catch (NonexistentEntityException e) {
// ignore as the previous test should prevent this.
}
}
}
So sorry, but I'm an idiot! I was not calling the service class that I thought I was. Fixed that and everything works as expected. Once again, sorry folks.
Regards
Remove the
user = em.merge(user);
statement in your DAO destroy method. I am not sure if it causes the probem, but it is not needed because the user is loaded in the statement before.

Resources