简体   繁体   中英

Triggering and Outputting events with Jersey SSE

I am new to using SSE with Jersey, and have the following situation.

I have a JAXB annotated class that represents and acts on the I/O of a Raspberry Pi (class GpioRepresentation).
The client class accesses the status of I/O through the method getUpdate() which returns the XML object representation of the class.

@XmlRootElement
public class GpioRepresentation implements GpioSubject
{

...

    /**
         * Returns an object of this class with the current
         * representation of the I/O states
         * @return this
         */
        public synchronized GpioRepresentation getUpdate() 
        {
            this.getGarageDoorInputState();
            this.getZoneOneFeedback();
            this.getZoneTwoFeedback();
            this.getZoneThreeFeedback();
            this.getGarageDoorRelayState();
            this.getZoneOneRelayState();
            this.getZoneTwoRelayState();
            this.getZoneThreeRelayState();
            return this;
        }
...

}

The client that uses getUpdate() is the class HomeResource, method getPiStatusStream(). This is a JAX-RS annotated method, and provides remote clients Server Sent Events. Currently this method is written as shown here with a continuous loop in a separate thread which polls for updates.

@Path("/homeservice")
@RolesAllowed({"ADMIN", "USER"})
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
public class HomeResource 
{
    private static final Logger LOGGER = LoggerFactory.getLogger(HomeResource.class);

    private GpioRepresentation piService;

    ...

    /**
         * gets status information on the Raspberry Pi's
         * I/O and returns it to the client on a continuous basis
         * and only if it changes.
         * @return EventOutput 
         */
        @GET
        @Path("/iostatus")
        @Produces(SseFeature.SERVER_SENT_EVENTS)
        public EventOutput getPiStatusStream() 
        {
            final EventOutput eventOutput = new EventOutput();

            new Thread(new Runnable() 
            {
                public void run()
                {

                    try {
                         String gdState = null;
                         String zOneState = null;
                         String zTwoState = null;
                         String zThreeState = null;
                         String gdRState = null;
                         String zOneRState = null;
                         String zTwoRState = null;
                         String zThreeRState = null;
                         String lastgdState = null;
                         String lastzOneState = null;
                         String lastzTwoState = null;
                         String lastzThreeState = null;
                         String lastgdRState = null;
                         String lastzOneRState = null;
                         String lastzTwoRState = null;
                         String lastzThreeRState = null;


                        while(true) {

                            final OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();

                            final GpioRepresentation iostatus = piService.getUpdate();

                            gdState = piService.getGarageDoorInputState();
                            zOneState = piService.getZoneOneFeedback();
                            zTwoState = piService.getZoneTwoFeedback();
                            zThreeState = piService.getZoneThreeFeedback();
                            gdRState = piService.getGarageDoorRelayState();
                            zOneRState = piService.getZoneOneRelayState();
                            zTwoRState = piService.getZoneTwoRelayState();
                            zThreeRState = piService.getZoneThreeRelayState();

                            if (!(gdState.equals(lastgdState) && zOneState.equals(lastzOneState) && zTwoState.equals(lastzTwoState) && zThreeState.equals(lastzThreeState)
                                    && gdRState.equals(lastgdRState) && zOneRState.equals(lastzOneRState) && zTwoRState.equals(lastzTwoRState) && zThreeRState.equals(lastzThreeRState)))
                            {
                                OutboundEvent event = eventBuilder.data(GpioRepresentation.class, iostatus)
                                        .mediaType(MediaType.APPLICATION_XML_TYPE)
                                        .build();

                                eventOutput.write(event);

                                lastgdState = gdState;
                                lastzOneState = zOneState;
                                lastzTwoState = zTwoState;
                                lastzThreeState = zThreeState;
                                lastgdRState = gdRState;
                                lastzOneRState = zOneRState;
                                lastzTwoRState = zTwoRState;
                                lastzThreeRState = zThreeRState;

                            }

                            Thread.sleep(100);
                        }
                    } 
                    catch (Exception exeption) 
                        {
                            System.err.println("Error: " + exeption);
                        } 
                    finally 
                        {
                            try 
                                {
                                    eventOutput.close();
                                } 
                            catch (IOException ioClose) 
                                {
                                    throw new RuntimeException("Error when closing the event output.", ioClose);
                                }
                        }
                    }
            }).start();

            return eventOutput;
        }

        ...

}

The issue I have and see with this is that this doesn't scale well. Creating a thread for every GET from a remote client takes time, and eats CPU resources. Plus I don't think this is an elegant solution. What I would like to do is encapsulate the event code into a separate class, and use some sort of observer pattern that can trigger the creation of an event....however, how do I tie this into the resource method so that it can be returned to the remote client?

Can anyone point me to some examples, or provide advice on designing a solution for this?

Solution was to utilize the SseBroadcaster class. I made the HomeService class an observer of the GpioRepresentation class, and then called a new method (broadcastIOUpdateMessage()) which then output my event to the remote client.

public void broadcastIOUpdateMessage()
{
    GpioRepresentation iostatus = piService.getUpdate();
    OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
    OutboundEvent event = eventBuilder.data(GpioRepresentation.class, iostatus)
            .mediaType(MediaType.APPLICATION_XML_TYPE)
            .build();

    broadcaster.broadcast(event);
}

@GET
@Path("/iostatus")
@Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput getPiStatusStream() 
{
    final EventOutput eventOutput = new EventOutput();
    this.broadcaster.add(eventOutput);
    return eventOutput;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM