/** * Read event data as a given generic type. * * @param type generic type to be used for event data de-serialization. * @param mediaType {@link MediaType media type} to be used for event data de-serialization. * @return event data de-serialized as an instance of a given type. * @throws javax.ws.rs.ProcessingException when provided type can't be read. The thrown exception wraps the original cause. * @since 2.3 */ public <T> T readData(GenericType<T> type, MediaType mediaType) { final MediaType effectiveMediaType = mediaType == null ? this.mediaType : mediaType; final MessageBodyReader reader = messageBodyWorkers.getMessageBodyReader(type.getRawType(), type.getType(), annotations, mediaType); if (reader == null) { throw new MessageBodyProviderNotFoundException(LocalizationMessages.EVENT_DATA_READER_NOT_FOUND()); } return readAndCast(type, effectiveMediaType, reader); }
@Override public void open() { if (!state.compareAndSet(EventProcessor.State.READY, EventProcessor.State.OPEN)) { switch (state.get()) { case CLOSED: throw new IllegalStateException(LocalizationMessages.EVENT_SOURCE_ALREADY_CLOSED()); case OPEN: throw new IllegalStateException(LocalizationMessages.EVENT_SOURCE_ALREADY_CONNECTED()); } } EventProcessor processor = EventProcessor .builder(endpoint, state, clientExecutor, this::onEvent, this::close) .reconnectDelay(reconnectDelay, reconnectTimeUnit) .build(); clientExecutor.submit(processor); // return only after the first request to the SSE endpoint has been made processor.awaitFirstContact(); }
/** * Await the initial contact with the SSE endpoint. */ public void awaitFirstContact() { LOGGER.debugLog("Awaiting first contact signal."); try { if (firstContactSignal == null) { return; } try { firstContactSignal.await(); } catch (InterruptedException ex) { LOGGER.log(CONNECTION_ERROR_LEVEL, LocalizationMessages.EVENT_SOURCE_OPEN_CONNECTION_INTERRUPTED(), ex); Thread.currentThread().interrupt(); } } finally { LOGGER.debugLog("First contact signal released."); } }
/** * Set event data and java type of event data. * * Type information will be used for {@link javax.ws.rs.ext.MessageBodyWriter} lookup. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param type java type of supplied data. Must not be {@code null}. * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case either {@code type} or {@code data} parameter is {@code null}. */ public Builder data(Class type, Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } if (type == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_TYPE_NULL()); } this.type = new GenericType(type); this.data = data; return this; }
private void processField(final InboundEvent.Builder inboundEventBuilder, final String name, final MediaType mediaType, final byte[] value) { final String valueString = new String(value, MessageUtils.getCharset(mediaType)); if ("event".equals(name)) { inboundEventBuilder.name(valueString); } else if ("data".equals(name)) { inboundEventBuilder.write(value); inboundEventBuilder.write(EOL_DATA); } else if ("id".equals(name)) { inboundEventBuilder.id(valueString); } else if ("retry".equals(name)) { try { inboundEventBuilder.reconnectDelay(Long.parseLong(valueString)); } catch (final NumberFormatException ex) { LOGGER.log(Level.FINE, LocalizationMessages.IN_EVENT_RETRY_PARSE_ERROR(valueString), ex); } } else { LOGGER.fine(LocalizationMessages.IN_EVENT_FIELD_NOT_RECOGNIZED(name, valueString)); } } }
if (!executor.awaitTermination(timeout, unit)) { LOGGER.log(CONNECTION_ERROR_LEVEL, LocalizationMessages.EVENT_SOURCE_SHUTDOWN_TIMEOUT(target.getUri().toString())); return false; LocalizationMessages.EVENT_SOURCE_SHUTDOWN_INTERRUPTED(target.getUri().toString())); Thread.currentThread().interrupt(); return false;
/** * Set event data and java type of event data. * * This is a convenience method that derives the event data type information from the runtime type of * the event data. The supplied event data may be represented as {@link javax.ws.rs.core.GenericEntity}. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case the {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } return data(ReflectionHelper.genericTypeFor(data), data); }
private void checkClosed() { if (isClosed()) { throw new IllegalStateException(LocalizationMessages.EVENT_SOURCE_ALREADY_CLOSED()); } } }
/** * Set event data and a generic java type of event data. * * Type information will be used for {@link javax.ws.rs.ext.MessageBodyWriter} lookup. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param type generic type of supplied data. Must not be {@code null}. * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case either {@code type} or {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(GenericType type, Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } if (type == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_TYPE_NULL()); } this.type = type; if (data instanceof GenericEntity) { this.data = ((GenericEntity) data).getEntity(); } else { this.data = data; } return this; }
private void processField(final InboundEvent.Builder inboundEventBuilder, final String name, final MediaType mediaType, final byte[] value) { final String valueString = new String(value, MessageUtils.getCharset(mediaType)); if ("event".equals(name)) { inboundEventBuilder.name(valueString); } else if ("data".equals(name)) { inboundEventBuilder.write(value); inboundEventBuilder.write(EOL_DATA); } else if ("id".equals(name)) { inboundEventBuilder.id(valueString); } else if ("retry".equals(name)) { try { inboundEventBuilder.reconnectDelay(Long.parseLong(valueString)); } catch (final NumberFormatException ex) { LOGGER.log(Level.FINE, LocalizationMessages.IN_EVENT_RETRY_PARSE_ERROR(valueString), ex); } } else { LOGGER.fine(LocalizationMessages.IN_EVENT_FIELD_NOT_RECOGNIZED(name, valueString)); } } }
if (!executor.awaitTermination(timeout, unit)) { LOGGER.log(CONNECTION_ERROR_LEVEL, LocalizationMessages.EVENT_SOURCE_SHUTDOWN_TIMEOUT(target.getUri().toString())); return false; LocalizationMessages.EVENT_SOURCE_SHUTDOWN_INTERRUPTED(target.getUri().toString())); Thread.currentThread().interrupt(); return false;
/** * Set event data and java type of event data. * * This is a convenience method that derives the event data type information from the runtime type of * the event data. The supplied event data may be represented as {@link javax.ws.rs.core.GenericEntity}. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case the {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } return data(ReflectionHelper.genericTypeFor(data), data); }
/** * Set event data and a generic java type of event data. * * Type information will be used for {@link javax.ws.rs.ext.MessageBodyWriter} lookup. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param type generic type of supplied data. Must not be {@code null}. * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case either {@code type} or {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(GenericType type, Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } if (type == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_TYPE_NULL()); } this.type = type; this.data = data; return this; }
/** * Open the connection to the supplied SSE underlying {@link WebTarget web target} and start processing incoming * {@link InboundEvent events}. * * @throws IllegalStateException in case the event source has already been opened earlier. */ public void open() { if (!state.compareAndSet(EventProcessor.State.READY, EventProcessor.State.OPEN)) { switch (state.get()) { case OPEN: throw new IllegalStateException(LocalizationMessages.EVENT_SOURCE_ALREADY_CONNECTED()); case CLOSED: throw new IllegalStateException(LocalizationMessages.EVENT_SOURCE_ALREADY_CLOSED()); } } EventProcessor.Builder builder = EventProcessor.builder(target, state, executor, this, shutdownHandler) .boundListeners(boundListeners) .unboundListeners(unboundListeners) .reconnectDelay(reconnectDelay, TimeUnit.MILLISECONDS); if (disableKeepAlive) { builder.disableKeepAlive(); } EventProcessor processor = builder.build(); executor.submit(processor); // return only after the first request to the SSE endpoint has been made processor.awaitFirstContact(); }
private void processField(final InboundEvent.Builder inboundEventBuilder, final String name, final MediaType mediaType, final byte[] value) { final String valueString = new String(value, MessageUtils.getCharset(mediaType)); if ("event".equals(name)) { inboundEventBuilder.name(valueString); } else if ("data".equals(name)) { inboundEventBuilder.write(value); inboundEventBuilder.write(EOL_DATA); } else if ("id".equals(name)) { inboundEventBuilder.id(valueString); } else if ("retry".equals(name)) { try { inboundEventBuilder.reconnectDelay(Long.parseLong(valueString)); } catch (final NumberFormatException ex) { LOGGER.log(Level.FINE, LocalizationMessages.IN_EVENT_RETRY_PARSE_ERROR(valueString), ex); } } else { LOGGER.fine(LocalizationMessages.IN_EVENT_FIELD_NOT_RECOGNIZED(name, valueString)); } } }
if (!executor.awaitTermination(timeout, unit)) { LOGGER.log(CONNECTION_ERROR_LEVEL, LocalizationMessages.EVENT_SOURCE_SHUTDOWN_TIMEOUT(target.getUri().toString())); return false; LocalizationMessages.EVENT_SOURCE_SHUTDOWN_INTERRUPTED(target.getUri().toString())); Thread.currentThread().interrupt(); return false;
/** * Read event data as a given generic type. * * @param type generic type to be used for event data de-serialization. * @param mediaType {@link MediaType media type} to be used for event data de-serialization. * @return event data de-serialized as an instance of a given type. * @throws javax.ws.rs.ProcessingException when provided type can't be read. The thrown exception wraps the original cause. * @since 2.3 */ public <T> T readData(GenericType<T> type, MediaType mediaType) { final MediaType effectiveMediaType = mediaType == null ? this.mediaType : mediaType; final MessageBodyReader reader = messageBodyWorkers.getMessageBodyReader(type.getRawType(), type.getType(), annotations, mediaType); if (reader == null) { throw new IllegalStateException(LocalizationMessages.EVENT_DATA_READER_NOT_FOUND()); } return readAndCast(type, effectiveMediaType, reader); }
/** * Await the initial contact with the SSE endpoint. */ public void awaitFirstContact() { LOGGER.debugLog("Awaiting first contact signal."); try { if (firstContactSignal == null) { return; } try { firstContactSignal.await(); } catch (InterruptedException ex) { LOGGER.log(CONNECTION_ERROR_LEVEL, LocalizationMessages.EVENT_SOURCE_OPEN_CONNECTION_INTERRUPTED(), ex); Thread.currentThread().interrupt(); } } finally { LOGGER.debugLog("First contact signal released."); } } }
/** * Set event data and java type of event data. * * This is a convenience method that derives the event data type information from the runtime type of * the event data. The supplied event data may be represented as {@link javax.ws.rs.core.GenericEntity}. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case the {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } return data(ReflectionHelper.genericTypeFor(data), data); }
/** * Set event data and a generic java type of event data. * * Type information will be used for {@link javax.ws.rs.ext.MessageBodyWriter} lookup. * <p> * Note that multiple invocations of this method result in previous even data being replaced with new one. * </p> * * @param type generic type of supplied data. Must not be {@code null}. * @param data event data. Must not be {@code null}. * @return updated builder instance. * @throws NullPointerException in case either {@code type} or {@code data} parameter is {@code null}. * @since 2.3 */ public Builder data(GenericType type, Object data) { if (data == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_NULL()); } if (type == null) { throw new NullPointerException(LocalizationMessages.OUT_EVENT_DATA_TYPE_NULL()); } this.type = type; this.data = data; return this; }