Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[doc] Document use of @Transactional on controller via @MessageMapping [SPR-13384] #17965

Closed
spring-projects-issues opened this issue Aug 23, 2015 · 5 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: documentation A documentation task
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Caleb Cushing opened SPR-13384 and commented

The actual cause is a little speculative (I can't imagine any reason other than the default would cause it), but I have a controller that works as expected until I implement this interace.

package com.xenoterracide.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public interface Loggable {
    default Logger log() {
        return LoggerFactory.getLogger( this.getClass() );
    }
}

Here's my controller; I realize it's a bit verbose for a test, but as soon as I removed Loggable everything started working.

package com.xenoterracide.mmp.musicdb.controller.message;

import com.xenoterracide.mmp.domain.music.Seeder;
import com.xenoterracide.mmp.domain.repository.SeederRepository;
import com.xenoterracide.mmp.domain.repository.StationRepository;
import com.xenoterracide.mmp.domain.station.Seed;
import com.xenoterracide.mmp.domain.station.Station;
import com.xenoterracide.mmp.domain.station.exception.SeedNotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.inject.Inject;
import java.util.Optional;

@Controller
@MessageMapping( "/stations" )
public class StationMessageController {

    private final ApplicationContext context;
    private final StationRepository repository;

    @Inject
    StationMessageController( final ApplicationContext context, final StationRepository repository ) {
        this.context = context;
        this.repository = repository;
    }

    @Transactional
    @MessageMapping( "/create" )
    @SendTo( "/topic/stations/created" )
    public Station createStation( @Validated final Seed seed ) throws SeedNotFoundException {
        SeederRepository<?> seederRepository = context.getBean( seed.getType().getRepositoryClass() );
        Optional<? extends Seeder> optionalEntity = seederRepository.findOneById( seed.getSeederId() );

        Station station = optionalEntity.map( Station::new ).orElseThrow( () -> new SeedNotFoundException( seed ) );

        return repository.save( station );
    }

    @SendTo( "/topic/stations/seed-not-found" )
    @MessageExceptionHandler( SeedNotFoundException.class )
    public Seed notFound( final SeedNotFoundException e ) {
        return e.getSeed();
    }

}

By not working I mean that the controller methods are not called when broadcast to websockets: they seem to be ignored completely.


Affects: 4.1.7

Referenced from: commits 4ecb3d4

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

This works the same as with @RequestMapping methods. The @Transactional annotation means the controller is probably decorated with a JDK dynamic proxy (unless you explicitly configure it to proxy by class with CGLIB) in which case the mapping annotations must be on an interface. The HandlerMapping can't "see" them otherwise. This is explained in the docs on the Spring MVC side and also on the @RequestMapping annotation (see here).

We should update @MessageMapping and the reference on the WebSocket side for the same.

@spring-projects-issues
Copy link
Collaborator Author

Caleb Cushing commented

is there any possibility the code that does this could emit a warning or debug log of some kind? though maybe it did and I missed it...

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

We look at every bean to find annotated methods. When @Transactional is used we see a JDK dynamic proxy and only the methods and annotations present on the interfaces the controller implements. So basically we don't know about the annotations. If we did we'd register them.

It's a bit of a trade-off, the convenience of having @Transaction directly on web controller methods vs a dedicated service layer interface with @Transactional on it.

@spring-projects-issues
Copy link
Collaborator Author

Caleb Cushing commented

must be something I'm missing since it works without interfaces at all... but ok. seemed worth asking if the scanner could be made to warn.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Without interfaces you're probably getting a CGLLIB proxy.

@spring-projects-issues spring-projects-issues added type: documentation A documentation task in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.2.1 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

2 participants