package com.greglturnquist.springagram.backend; import static com.greglturnquist.springagram.backend.WebSocketConfiguration.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.rest.core.annotation.HandleAfterCreate; import org.springframework.data.rest.core.annotation.HandleAfterDelete; import org.springframework.data.rest.core.annotation.HandleAfterLinkDelete; import org.springframework.data.rest.core.annotation.HandleAfterLinkSave; import org.springframework.data.rest.core.annotation.HandleBeforeCreate; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.hateoas.EntityLinks; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; /** * Because the published application context events are, in fact, synchronous, this handler is able to * hold up {@link Item} creation until the {@link User} * can be retrieved and populated. * * Since this is in the same thread of execution as the original REST call, * {@link SecurityContextHolder} can be used to retrieve the username, * and hence do the user lookup. */ // tag::event-handler-one[] @Component @RepositoryEventHandler(Item.class) public class SpringDataRestEventHandler { // end::event-handler-one[] private static final Logger log = LoggerFactory.getLogger(SpringDataRestEventHandler.class); private final UserRepository repository; private final EntityLinks entityLinks; private final ResourceMappings resourceMappings; private final RepositoryRestConfiguration config; private final StringRedisTemplate redis; private final SimpMessagingTemplate websocket; @Autowired public SpringDataRestEventHandler(UserRepository repository, StringRedisTemplate redis, EntityLinks entityLinks, ResourceMappings resourceMappings, RepositoryRestConfiguration config, SimpMessagingTemplate websocket) { this.repository = repository; this.redis = redis; this.entityLinks = entityLinks; this.resourceMappings = resourceMappings; this.config = config; this.websocket = websocket; } // tag::event-handler-two[] @HandleBeforeCreate public void applyUserInformationUsingSecurityContext(Item item) { String name = SecurityContextHolder.getContext().getAuthentication().getName(); User user = repository.findByName(name); if (user == null) { User newUser = new User(); newUser.setName(name); user = repository.save(newUser); } item.setUser(user); } // end::event-handler-two[] // tag::event-handler-three[] @HandleAfterCreate public void notifyAllClientsAboutNewItem(Item item) { log.info("Just created new item " + item); publish("backend.newItem", pathFor(item)); } @HandleAfterDelete public void notifyAllClientsAboutItemDeletion(Item item) { log.info("Just deleted item " + item); publish("backend.deleteItem", pathFor(item)); } // end::event-handler-three[] @HandleAfterLinkDelete public void notifyAllClientsWhenRemovedFromGallery(Item item, Object obj) { log.info("Item " + item + " just had an afterLinkDelete..."); log.info("Related object => " + obj); publish("backend.removeItemFromGallery-item", pathFor(item)); publish("backend.removeItemFromGallery-gallery", pathFor((Gallery) obj)); } @HandleAfterLinkSave public void notifyAllClientsWhenAddedToGallery(Item item, Object obj) { log.info("Item " + item + " just had an afterLinkSave..."); publish("backend.addItemToGallery-item", pathFor(item)); publish("backend.addItemToGallery-gallery", pathFor(item.getGallery())); } private void publish(String routingKey, String message) { redis.convertAndSend(MESSAGE_PREFIX + "/" + routingKey, message); websocket.convertAndSend(MESSAGE_PREFIX + "/" + routingKey, message); } // tag::event-handler-four[] private String pathFor(Item item) { return entityLinks.linkForSingleResource(item.getClass(), item.getId()).toUri().getPath(); } // end::event-handler-four[] private String pathFor(Gallery gallery) { return entityLinks.linkForSingleResource(gallery.getClass(), gallery.getId()).toUri().getPath(); } }