Camel http component does not close connections - Close_Wait - java

Camel http component does not close connections properly?
Having below route I have observed that connections are being created on the server, but not terminated.
After a while this is causing a problem
java.io.IOException: Too many open files
route:
from("seda:testSeda?concurrentConsumers=20")
.setHeader("Connection", constant("Close"))
.to("http://testServer/testFile.xml?authMethod=Basic&throwExceptionOnFailure=false&authUsername=user&authPassword=password")
.to("file://abc")
.end();
connections are in Close_Wait state any ideas?
I am using camel-http lib in version 2.14

You can override default HttpClient used by Apache Camel and define a custom Keep Alive Strategy.
https://howtodoinjava.com/spring-boot2/resttemplate/resttemplate-httpclient-java-config/
The code bellow resolved my issue in production:
#Configuration
public class AppConfiguration {
#Autowired
private PoolingHttpClientConnectionManager poolingConnectionManager;
#Autowired
private ConnectionKeepAliveStrategy connectionKeepAliveStrategy;
#Autowired
private SSLConnectionSocketFactory sslContext;
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
HttpComponent httpComponent = context.getComponent("https4", HttpComponent.class);
httpComponent.setHttpClientConfigurer(new HttpClientConfigurer() {
#Override
public void configureHttpClient(HttpClientBuilder builder) {
builder.setSSLSocketFactory(sslContext);
RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslContext).build();
builder.setConnectionManager(poolingConnectionManager);
builder.setKeepAliveStrategy(connectionKeepAliveStrategy);
}
});
}
#Override
public void afterApplicationStart(CamelContext arg0) {
}
};
}
}
#Configuration
public class HttpClientConfig {
private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000;
private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 30;
#Value("${pathCertificado}")
private String pathCertificado;
private Logger logger = LoggerFactory.getLogger(HttpClientConfig.class);
#Bean
public PoolingHttpClientConnectionManager poolingConnectionManager() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
return connectionManager;
}
#Bean
public CloseableHttpClient httpClient() {
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setConnectTimeout(5000)
.setSocketTimeout(15000).build();
return HttpClientBuilder.create().setSSLSocketFactory(this.getSSLContext())
.setConnectionManager(this.poolingConnectionManager()).setDefaultRequestConfig(config)
.setKeepAliveStrategy(this.connectionKeepAliveStrategy()).build();
}
#Bean
public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
return new ConnectionKeepAliveStrategy() {
#Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return DEFAULT_KEEP_ALIVE_TIME_MILLIS;
}
};
}
#Bean
public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) {
return new Runnable() {
#Override
#Scheduled(fixedDelay = 10000)
public void run() {
if (connectionManager != null) {
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS);
}
}
};
}
#Bean
public SSLConnectionSocketFactory getSSLContext() {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream jksFile = new FileInputStream(this.pathCertificado)) {
keyStore.load(jksFile, "xxxxxx".toCharArray());
}
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, acceptingTrustStrategy).build();
return new SSLConnectionSocketFactory(sslContext);
} catch (Exception e) {
logger.error("Keystore load failed: " + this.pathCertificado, e);
return null;
}
}
}

Related

Hilt how to pass variable to singleton instance

I am trying to create DI for my api client. So I can access it all over the app without big problems.
I have implemented normal singleton but I would like to change it to hilt.
Problem is, My client class needs a baseURL to be set up dynamicly on startup of application.
private APIClient(String baseURL) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseURL)
.client(getUnsafeOkHttpClient().build())
.addConverterFactory(GsonConverterFactory.create())
.build();
sessionApi = retrofit.create(ISessionApi.class);
}
Is there a way to do it? I can't find anything similar.
Whole code:
public class APIClient {
private static APIClient instance = null;
private static String baseURL;
private ISessionApi sessionApi;
private APIClient(String baseURL) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseURL)
.client(getUnsafeOkHttpClient().build())
.addConverterFactory(GsonConverterFactory.create())
.build();
sessionApi = retrofit.create(ISessionApi.class);
}
public static synchronized APIClient getInstance() {
if (instance == null) {
instance = new APIClient(baseURL);
}
return instance;
}
public static synchronized APIClient getInstance(String baseURL) {
APIClient.baseURL=baseURL;
if (instance == null) {
instance = new APIClient(baseURL);
}
return instance;
}
public ISessionApi getSessionApi() {
return sessionApi;
}
public static OkHttpClient.Builder getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

Reuse PoolingHttpClientConnectionManager

I have implemented PoolingHttpClientConnectionManager. How can I make client reusable, so other class can reuse it? Should I put it in a Service?
public class MyClass() {
private static CloseableHttpClient client;
private static final CookieStore commonCookieStore = new BasicCookieStore();
private static final ScheduledExecutorService cleanUpThread = ScheduledExecutorService.wrap(
new ScheduledThreadPoolExecutor(1), "connection-pool-cleanup-thread-%d");
public MyClass() {
try {
String keystoreId = //getKeyStoreId()
if (client == null) {
if (StringUtils.isNotEmpty(keystoreId)) {
SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(TLSSocketFactoryCache.getInstance().get(keystoreId),
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConSocFactory).build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1000)
.setConnectionRequestTimeout(1000)
.setSocketTimeout(1000)
.build();
ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
// ignore
}
}
}
try {
return 60000;
} catch(NumberFormatException ignore) {
// ignore
}
// otherwise keep alive for 60 seconds
return 60 * 1000;
}
};
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setDefaultMaxPerRoute(200);
connectionManager.setMaxTotal(200);
connectionManager.setValidateAfterInactivity(60000);
client = HttpClients.custom().setConnectionManager(connectionManager)
.setConnectionManagerShared(true)
.setDefaultRequestConfig(requestConfig)
.setKeepAliveStrategy(keepAliveStrategy)
.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy())
.setUserTokenHandler(context -> null)
.setMaxConnTotal(200)
.setMaxConnPerRoute(200)
.setConnectionTimeToLive(60000, TimeUnit.MILLISECONDS)
.build();
int closeIdle = 60000;
cleanUpThread.scheduleAtFixedRate(() -> {
try {
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(closeIdle, TimeUnit.MILLISECONDS);
} catch (Exception ex) {
LOGGER.error("Exception on SGW clean up thread", ex);
}
}, closeIdle, closeIdle, TimeUnit.MILLISECONDS);
} else {
LOGGER.error("Error! No keystore specified");
}
}
} catch (Exception e) {
LOGGER.error("Encountered exception during retrieval of SSL Context", e);
}
}
}

How can I store a POJO in Redis with Micronaut framework?

I am new to Micronaut. I am trying to port a project to Micronaut (v1.1.1) and I have found a problem with Redis.
I am just trying to save a simple POJO in Redis, but when I try to "save" it the following error is raised:
io.lettuce.core.RedisException: io.netty.handler.codec.EncoderException: Cannot encode command. Please close the connection as the connection state may be out of sync.
Code is very simple (HERE you can find a complete test.):
class DummyTest {
#Test
public void testIssue() throws Exception {
final Date now = Date.from(Instant.now());
CatalogContent expectedContentOne = CatalogContent.builder()
.contentId(1)
.status(ContentStatus.AVAILABLE)
.title("uno")
.streamId(1)
.available(now)
.tags(Set.of("tag1", "tag2"))
.build();
repository.save(expectedContentOne);
}
}
/.../
class CatalogContentRepository {
private StatefulRedisConnection<String, CatalogContent> connection;
public CatalogContentRepository(StatefulRedisConnection<String, CatalogContent> connection) {
this.connection = connection;
}
public void save(CatalogContent content) {
RedisCommands<String, CatalogContent> redisApi = connection.sync();
redisApi.set(String.valueOf(content.getContentId()),content); //Error here!
}
}
Any idea will be welcomed.
Thanks in advance.
For the record I will answer my own question:
Right now (20190514) Micronaut only generate StatefulRedisConnection<String,String> with a hardcoded UTF8 String codec.
To change this you have to replace the DefaultRedisClientFactory and define a method returning the StatefulRedisConnection you need,
with your prefered codec.
In my case:
#Requires(beans = DefaultRedisConfiguration.class)
#Singleton
#Factory
#Replaces(factory = DefaultRedisClientFactory.class)
public class RedisClientFactory extends AbstractRedisClientFactory {
#Bean(preDestroy = "shutdown")
#Singleton
#Primary
#Override
public RedisClient redisClient(#Primary AbstractRedisConfiguration config) {
return super.redisClient(config);
}
#Bean(preDestroy = "close")
#Singleton
#Primary
public StatefulRedisConnection<String, Object> myRedisConnection(#Primary RedisClient redisClient) {
return redisClient.connect(new SerializedObjectCodec());
}
#Bean(preDestroy = "close")
#Singleton
#Primary
#Override
public StatefulRedisConnection<String, String> redisConnection(#Primary RedisClient redisClient) {
throw new RuntimeException("puta mierda");
}
#Override
#Bean(preDestroy = "close")
#Singleton
public StatefulRedisPubSubConnection<String, String> redisPubSubConnection(#Primary RedisClient redisClient) {
return super.redisPubSubConnection(redisClient);
}
}
Codec has been taken from Redis Lettuce wiki
public class SerializedObjectCodec implements RedisCodec<String, Object> {
private Charset charset = Charset.forName("UTF-8");
#Override
public String decodeKey(ByteBuffer bytes) {
return charset.decode(bytes).toString();
}
#Override
public Object decodeValue(ByteBuffer bytes) {
try {
byte[] array = new byte[bytes.remaining()];
bytes.get(array);
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(array));
return is.readObject();
} catch (Exception e) {
return null;
}
}
#Override
public ByteBuffer encodeKey(String key) {
return charset.encode(key);
}
#Override
public ByteBuffer encodeValue(Object value) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bytes);
os.writeObject(value);
return ByteBuffer.wrap(bytes.toByteArray());
} catch (IOException e) {
return null;
}
}
}

Conscrypt with jdk8 to enable ALPN for http2

I have been searching how to implement Conscrypt SSL provider using conscrypt-openjdk-uber-1.4.1.jar for jdk8 to support ALPN for making a http2(using apache httpclient 5) connection to a server as jdk8 does not support ALPN by default or the other solution is to migrate to jdk9(or higher) which is not feasible for now as our product is heavily dependent on jdk8
I have been searching extensively for some docs or examples to implement but I could not find one.
I have tried to insert conscrypt provider as default and my program takes it as default provider but still it fails to connect with http2 server, my example is as follows,
public static void main(final String[] args) throws Exception {
Security.insertProviderAt(new OpenSSLProvider(), 1);
final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustAllStrategy()).build();
final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create().setTlsStrategy(new H2TlsStrategy(sslContext, NoopHostnameVerifier.INSTANCE)).build();
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setSoTimeout(Timeout.ofSeconds(5)).build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, null, ioReactorConfig, connectionManager);
client.start();
final HttpHost target = new HttpHost("localhost", 8082, "https");
final Future<AsyncClientEndpoint> leaseFuture = client.lease(target, null);
final AsyncClientEndpoint endpoint = leaseFuture.get(10, TimeUnit.SECONDS);
try {
String[] requestUris = new String[] {"/"};
CountDownLatch latch = new CountDownLatch(requestUris.length);
for (final String requestUri: requestUris) {
SimpleHttpRequest request = SimpleHttpRequest.get(target, requestUri);
endpoint.execute(SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback<SimpleHttpResponse>() {
#Override
public void completed(final SimpleHttpResponse response) {
latch.countDown();
System.out.println(requestUri + "->" + response.getCode());
System.out.println(response.getBody());
}
#Override
public void failed(final Exception ex) {
latch.countDown();
System.out.println(requestUri + "->" + ex);
ex.printStackTrace();
}
#Override
public void cancelled() {
latch.countDown();
System.out.println(requestUri + " cancelled");
}
});
}
latch.await();
} catch (Exception e) {
e.printStackTrace();
}finally {
endpoint.releaseAndReuse();
}
client.shutdown(ShutdownType.GRACEFUL);
}
this programs gives the output as
org.apache.hc.core5.http.ConnectionClosedException: Connection closed
org.apache.hc.core5.http.ConnectionClosedException: Connection closed
at org.apache.hc.core5.http2.impl.nio.FrameInputBuffer.read(FrameInputBuffer.java:146)
at org.apache.hc.core5.http2.impl.nio.AbstractHttp2StreamMultiplexer.onInput(AbstractHttp2StreamMultiplexer.java:415)
at org.apache.hc.core5.http2.impl.nio.AbstractHttp2IOEventHandler.inputReady(AbstractHttp2IOEventHandler.java:63)
at org.apache.hc.core5.http2.impl.nio.ClientHttp2IOEventHandler.inputReady(ClientHttp2IOEventHandler.java:38)
at org.apache.hc.core5.reactor.InternalDataChannel.onIOEvent(InternalDataChannel.java:117)
at org.apache.hc.core5.reactor.InternalChannel.handleIOEvent(InternalChannel.java:50)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.processEvents(SingleCoreIOReactor.java:173)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:123)
at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:80)
at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
at java.lang.Thread.run(Thread.java:748)
If I print the provider and version it prints as Conscrypt version 1.0 and JDK 1.8.0_162, but still it fails to connect with a http2 endpoint
same chunk of code works perfectly if I connect using jdk9 with default provider, what I m missing here in conscrypt configuration?
Any help is appreciated
Thanks in advance
Just replacing the default JSSE provider with Conscrypt is not enough. One also needs a custom TlsStrategy that can take advantage of Conscrypt APIs.
This what works for me with Java 1.8 and Conscrypt 1.4.1
static class ConscriptClientTlsStrategy implements TlsStrategy {
private final SSLContext sslContext;
public ConscriptClientTlsStrategy(final SSLContext sslContext) {
this.sslContext = Args.notNull(sslContext, "SSL context");
}
#Override
public boolean upgrade(
final TransportSecurityLayer tlsSession,
final HttpHost host,
final SocketAddress localAddress,
final SocketAddress remoteAddress,
final Object attachment) {
final String scheme = host != null ? host.getSchemeName() : null;
if (URIScheme.HTTPS.same(scheme)) {
tlsSession.startTls(
sslContext,
host,
SSLBufferMode.STATIC,
(endpoint, sslEngine) -> {
final SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setProtocols(H2TlsSupport.excludeBlacklistedProtocols(sslParameters.getProtocols()));
sslParameters.setCipherSuites(H2TlsSupport.excludeBlacklistedCiphers(sslParameters.getCipherSuites()));
H2TlsSupport.setEnableRetransmissions(sslParameters, false);
final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
(HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
final String[] appProtocols;
switch (versionPolicy) {
case FORCE_HTTP_1:
appProtocols = new String[] { ApplicationProtocols.HTTP_1_1.id };
break;
case FORCE_HTTP_2:
appProtocols = new String[] { ApplicationProtocols.HTTP_2.id };
break;
default:
appProtocols = new String[] { ApplicationProtocols.HTTP_2.id, ApplicationProtocols.HTTP_1_1.id };
}
if (Conscrypt.isConscrypt(sslEngine)) {
sslEngine.setSSLParameters(sslParameters);
Conscrypt.setApplicationProtocols(sslEngine, appProtocols);
} else {
H2TlsSupport.setApplicationProtocols(sslParameters, appProtocols);
sslEngine.setSSLParameters(sslParameters);
}
},
(endpoint, sslEngine) -> {
if (Conscrypt.isConscrypt(sslEngine)) {
return new TlsDetails(sslEngine.getSession(), Conscrypt.getApplicationProtocol(sslEngine));
}
return null;
});
return true;
}
return false;
}
}
public static void main(String[] args) throws Exception {
final SSLContext sslContext = SSLContexts.custom()
.setProvider(Conscrypt.newProvider())
.build();
final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new ConscriptClientTlsStrategy(sslContext))
.build();
try (CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
.setConnectionManager(cm)
.build()) {
client.start();
final HttpHost target = new HttpHost("nghttp2.org", 443, "https");
final String requestUri = "/httpbin";
final HttpClientContext clientContext = HttpClientContext.create();
final SimpleHttpRequest request = SimpleHttpRequests.GET.create(target, requestUri);
final Future<SimpleHttpResponse> future = client.execute(
SimpleRequestProducer.create(request),
SimpleResponseConsumer.create(),
clientContext,
new FutureCallback<SimpleHttpResponse>() {
#Override
public void completed(final SimpleHttpResponse response) {
System.out.println(requestUri + "->" + response.getCode() + " " +
clientContext.getProtocolVersion());
System.out.println(response.getBody());
final SSLSession sslSession = clientContext.getSSLSession();
if (sslSession != null) {
System.out.println("SSL protocol " + sslSession.getProtocol());
System.out.println("SSL cipher suite " + sslSession.getCipherSuite());
}
}
#Override
public void failed(final Exception ex) {
System.out.println(requestUri + "->" + ex);
}
#Override
public void cancelled() {
System.out.println(requestUri + " cancelled");
}
});
future.get();
System.out.println("Shutting down");
client.shutdown(CloseMode.GRACEFUL);
}
}

How to call the websocket server to sends the message to the client in spring

My project uses spring framework
WebSocketConfig.java
#Configuration
#EnableWebMvc
#EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(systemWebSocketHandler(),"/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor());
registry.addHandler(systemWebSocketHandler(), "/sockjs/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor())
.withSockJS();
}
#Bean
public WebSocketHandler systemWebSocketHandler(){
return new SystemWebSocketHandler();
}
}
SystemWebSocketHandler.java
public class SystemWebSocketHandler implements WebSocketHandler {
private static final Logger logger;
private static final ArrayList<WebSocketSession> users;
static {
users = new ArrayList<>();
logger = LoggerFactory.getLogger(SystemWebSocketHandler.class);
}
#Autowired
private WebSocketService webSocketService;
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.debug("connect to the websocket success......");
users.add(session);
String userName = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);
//查询未读消息
int count = webSocketService.getUnReadNews((String)session.getAttributes().get(Constants.WEBSOCKET_USERNAME));
session.sendMessage(new TextMessage(count+""));
}
#Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
}
#Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
logger.debug("websocket connection closed......");
users.remove(session);
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
logger.debug("websocket connection closed......");
users.remove(session);
}
#Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 给所有在线用户发送消息
*
* #param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 给某个用户发送消息
*
* #param userName
* #param message
*/
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
my jsp client
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/Origami/webSocketServer");
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8080/Origami/webSocketServer");
} else {
websocket = new SockJS("http://localhost:8080/Origami/sockjs/webSocketServer");
}
this is my websocket code and it works well
now I want to send messages to the client in a controller ,this is my controller
#Controller
public class AdminController {
static Logger logger = LoggerFactory.getLogger(AdminController.class);
#Autowired(required = false)
private AdminService adminService;
#Autowired(required = false)
private SystemWebSocketHandler systemWebSocketHandler;
#RequestMapping("/auditing")
#ResponseBody
public String auditing(HttpServletRequest request){
String result = "fail";
int id = Integer.parseInt(request.getParameter("id"));
String reason = request.getParameter("reason");
String title = request.getParameter("title");
String username = request.getParameter("username");
News news = new News();
DateTime dateTime = DateTime.now();
news.setNewsTime(dateTime.toDate());
news.setState(0);
news.setUsername(username);
if(reason.equals("")){
result = adminService.auditingById(id,"Y");
news.setNewsContent(String.format(Constants.AUDIT_MESSAGE, username, title, reason));
adminService.addNewsWithUnAudit(news);
}else{
news.setNewsContent(String.format(Constants.UN_AUDIT_MESSAGE,username,title,reason));
result = adminService.addNewsWithUnAudit(news);
result = adminService.auditingById(id, "D");
}
//SystemServerEndPoint serverEndPoint = new SystemServerEndPoint();
int unReadNewsCount = adminService.getUnReadNews(username);
systemWebSocketHandler.sendMessageToUser(username, new TextMessage(unReadNewsCount + ""));
return result;
}
}
I want to call
systemWebSocketHandler.sendMessageToUser(username, new TextMessage(unReadNewsCount + ""));
to send message to the client but systemWebSocketHandler is null
How to inject the systemWebSocketHandler to the controller
or some other ideas to complete the required? Such as the server connect to the websocketserver when it need to send message to the client and closed when it finished
My English is poor, but I'm trying to learn
I have resolved the problem
#Controller
public class AdminController {
#Bean
public SystemWebSocketHandler systemWebSocketHandler() {
return new SystemWebSocketHandler();
}

Categories