코린이의 기록

[java] com.aliyun.openservices.iot.api.exception.IotClientException: client is already connected! 본문

JAVA

[java] com.aliyun.openservices.iot.api.exception.IotClientException: client is already connected!

코린이예요 2019. 3. 4. 18:27
반응형

증상

Login success handler에서 mqttClient Listner 등록 및 connect를 시도하는데, 

문제는 로그아웃 후에 다시 Login하면 아래와 같은 오류가 발생한다.


org.apache.catalina.core.StandardWrapperValve invoke 

심각: Servlet.service() for servlet [dispatcherServlet] in context with path [/aui] threw exception

com.aliyun.openservices.iot.api.exception.IotClientException: client is already connected!

오류가 발생함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3월 04, 2019 6:08:50 오후 org.apache.catalina.core.StandardWrapperValve invoke
심각: Servlet.service() for servlet [dispatcherServlet] in context with path [/aui] threw exception
com.aliyun.openservices.iot.api.exception.IotClientException: client is already connected!
    at com.aliyun.openservices.iot.api.message.impl.MessageClientImpl.connect(MessageClientImpl.java:302)
    at com.alticast.ota.administrator.security.LoginSuccessHandler.onAuthenticationSuccess(LoginSuccessHandler.java:54)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:298)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:105)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:118)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:962)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1115)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
 
cs



오류 메세지를 찍는 부분을 타고 들어가보니 connect 메소드에서 throw new IotClientException에 걸렸다. 


MessageClientImpl.class 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 @Override
    public void connect(MessageCallback messageCallback) {
        if (started.compareAndSet(falsetrue)) {
            setMessageCallback(messageCallback);
            messageCallbackExecutorService = new ThreadPoolExecutor(profile.getCallbackThreadCorePoolSize(),
                profile.getCallbackThreadMaximumPoolSize(), 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(profile.getCallbackThreadBlockingQueueSize()),
                new ThreadFactoryBuilder().setDaemon(true).setNameFormat("iot-message-client-receiver-%d").build());
            publishRetryExecutorService = new ScheduledThreadPoolExecutor(1new ThreadFactoryBuilder().setNameFormat(
                "iot-message-client-schedule-thread").build());
            this.client = new IotHttp2Client(profile, profile.isMultiConnection() ? -1 : 1);
            doConnect();
        } else {
            throw new IotClientException("client is already connected!");
        }
    }
cs



  • compareAndSet

    public final boolean compareAndSet(boolean expect,
                                       boolean update)
    Atomically sets the value to the given updated value if the current value == the expected value.
    Parameters:
    expect - the expected value
    update - the new value
    Returns:
    true if successful. False return indicates that the actual value was not equal to the expected value.

원인 분석

1
   private AtomicBoolean started;
cs

line 3 에서 started.compareAndSet()을 보면 started 값을 비교하여 connect 상태를 확인한다. 즉 started 값이 false면 update값(true)으로 바꾼다. disconnect된 상태에서는 started 값이 false이므로(boolean 초기값은 false) boolean expect == false 가 true일 것이고 그렇다면 started 값을 boolean update 값인 true로 바꿈으로써 connect가 됨을 뜻하게된다. 

결론은, 내 소스에서는 disconnect하는 부분이 없어서 started가 true인 상태에서 connect를 시도하려고 하기때문에 else문으로 빠진것. 



해결 방법

1
2
3
4
    @Override
    public boolean isConnected() {
        return isConnected.get();
    }
cs

위 메소드를 호출하여 isConnected인지 확인하고 isConnected이면 disconnect 시켜서 다시 connect 하는 방식으로 우회(?)할 수 있으나.. 그 방법은 근본적인 해결방법이 아닌것 같아서 세션 타임아웃과 log out시 disconnect를 수행하는 로직을 추가해야 할것.

반응형
Comments