Kaloyan Pashov
Kaloyan Pashov

Reputation: 165

Share information between Spring components

I am trying to build an app where I implement a rather simple pattern: I have a websocket server that receives streaming data from a number of clients. This data is then stored so that it can be served through a RESTful API. I want to have some sort of interface that both the WS server and the REST API can access to both store and request the latest data.

To present a simplified case:

I have 10 connected gadgets that are streaming messages over a websocket connection. Each one of them is streaming to /ingest/{ID}. I then have a REST API that returns latest message on the /latest/{ID}.

  1. Is this the correct design pattern to go for?
  2. If so, how do I implement it using as much of the Spring framework as possible?

I am imagining that I need to do the following:

I will have one class with the main(String args[]) method. This will then run itself and the Spring framework will pick up the different components- the WebSocket endpoint, the REST endpoint, and the DataCache class.

How do I give the correct instance of my DataCache class to the WebSocket and the REST endpoint?

        @SpringBootApplication
        @EnableWebSocket
        @ComponentScan(basePackages={"com.acme.server.wsendpoint","com.acme.server.restendpoint","com.acme.server.datacache"})
        public class PredixWebsocketServerApplication{
            private static Logger log = LoggerFactory.getLogger(WebsocketServerApplication.class);

            /**
             * @param args -
             */
            @SuppressWarnings("nls")
            public static void main(String[] args) {
                SpringApplication app = new SpringApplication(WebsocketServerApplication.class);
            }
        }

Then, I would have the following code in each of the component classes:

    public class WSEndpoint {

        private static Logger logger = LoggerFactory.getLogger(WSEndpoint.class);
        private DataCache dataCache;

        public WSEndpoint(DataCache cache) {
            dataCache = cache;
//do init stuff
        }

    /**
     * @param ID - machine ID
     * @param session - session object
     * @param ec      -
     */
    @OnOpen
    public void onOpen(@PathParam(value = "mID") String ID, @PathParam(value = "direction") String DirectionID, final Session session, EndpointConfig ec) {
        logger.info("Server: opened... for Direction: " + DirectionID + " Node Id : " + ID + " : " + session.getId());
    }

    /**
     * @param ID -
     * @param message -
     * @param session -
     */
    @OnMessage
    public void onMessage(@PathParam(value = "modelID") String modelID, @PathParam(value = "direction") String DirectionID, String message, Session session) {
        try {
            for(Session s : session.getOpenSessions()){
                if ("ingest".equals(s.getPathParameters().get("direction"))){
                    DataCache.store(message);
                }
            }
        } catch (Exception e){
            logger.error("Error handling websocket message: " + e.getLocalizedMessage());
        }
    }

    /**
     * @param session     - session object
     * @param closeReason - The reason of close of session
     */
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info("Server: Session " + session.getId() + " closed because of " + closeReason.toString());
    }

    /**
     * @param session - current session object
     * @param t       - Throwable instance containing error info
     */
    @OnError
    public void onError(Session session, Throwable t) {
        logger.error("Server: Session " + session.getId() + " closed because of " + t.getMessage());
    }


}

Then, the rest endpoint will look something like this:

@Controller
public class RestEndpoint {

    private DataCache dataCache;
    private static Logger log = LoggerFactory.getLogger(restEndpoint.class);

    public void setGreeting(DataCache cache){
        dataCache = cache;
    }

    @CrossOrigin(origins = "http://localhost:5000")
    @RequestMapping(value="/latest/{ID}")
    @ResponseBody
    public String latest() {
        log.info("Sending message: " + DataCache.getLatest(ID));
        return latest;
    }
}

Upvotes: 2

Views: 4637

Answers (1)

Jeremy Grand
Jeremy Grand

Reputation: 2370

Use the Spring IoC Container to inject your DataCache into both of your Beans, that's the exact purpose of Spring.

try

@Autowired
private DataCache datacache

In both your EndPoints.

If the Spring autoConfiguration is enabled, annotate your DataCache class with @Service to tell Spring to create a DataCache Bean.

And you should read more about in https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html

Upvotes: 2

Related Questions