All Downloads are FREE. Search and download functionalities are using the official Maven repository.

link.thingscloud.freeswitch.esl.inbound.AbstractInboundClient Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package link.thingscloud.freeswitch.esl.inbound;

import io.netty.channel.ChannelFutureListener;
import io.netty.util.concurrent.DefaultThreadFactory;
import link.thingscloud.freeswitch.esl.InboundClient;
import link.thingscloud.freeswitch.esl.constant.EslConstant;
import link.thingscloud.freeswitch.esl.exception.InboundClientException;
import link.thingscloud.freeswitch.esl.inbound.handler.InboundChannelHandler;
import link.thingscloud.freeswitch.esl.inbound.listener.EventListener;
import link.thingscloud.freeswitch.esl.inbound.listener.ServerOptionListener;
import link.thingscloud.freeswitch.esl.inbound.option.ConnectState;
import link.thingscloud.freeswitch.esl.inbound.option.InboundClientOption;
import link.thingscloud.freeswitch.esl.inbound.option.ServerOption;
import link.thingscloud.freeswitch.esl.transport.CommandResponse;
import link.thingscloud.freeswitch.esl.transport.event.EslEvent;
import link.thingscloud.freeswitch.esl.transport.message.EslHeaders;
import link.thingscloud.freeswitch.esl.transport.message.EslMessage;
import link.thingscloud.freeswitch.esl.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 

flow :

* |------------------------------------| * | | * \| |---» CONNECTED ---» CLOSED ---» SHUTDOWN * INIT ----» CONNECTING -----| * /| |---» FAILED * | | * ----------------------| * * @author : zhouhailin */ abstract class AbstractInboundClient extends AbstractNettyInboundClient implements InboundClient { private final ScheduledThreadPoolExecutor scheduledPoolExecutor = new ScheduledThreadPoolExecutor(1, new DefaultThreadFactory("scheduled-pool", true)); private final Map handlerTable = new HashMap<>(32); AbstractInboundClient(InboundClientOption option) { super(option); } /** * {@inheritDoc} */ @Override public InboundClientOption option() { return option; } /** * {@inheritDoc} */ @Override public void start() { log.info("inbound client will start ..."); addServerOptionListener(); addEventListener(); option().serverOptions().forEach(serverOption -> { if (serverOption.state() == ConnectState.INIT) { serverOption.state(ConnectState.CONNECTING); doConnect(serverOption); } }); } /** * {@inheritDoc} */ @Override public void shutdown() { log.info("inbound client will shutdown ..."); option().serverOptions().forEach(serverOption -> { serverOption.state(ConnectState.SHUTDOWN); InboundChannelHandler inboundChannelHandler = handlerTable.get(serverOption.addr()); if (inboundChannelHandler != null) { inboundChannelHandler.close().addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { log.info("shutdown inbound client remote server [{}:{}] success.", serverOption.host(), serverOption.port()); } else { log.info("shutdown inbound client remote server [{}:{}] failed, cause : ", serverOption.host(), serverOption.port(), future.cause()); } }); } }); workerGroup.shutdownGracefully(); } /** * {@inheritDoc} */ @Override public void onChannelActive(String remoteAddr, InboundChannelHandler inboundChannelHandler) { handlerTable.put(remoteAddr, inboundChannelHandler); // 连接监听 option().serverOptions().forEach(serverOption -> { if (StringUtils.equals(serverOption.addr(), remoteAddr)) { if (option().serverConnectionListener() != null) { option().serverConnectionListener().onOpened(serverOption); } } }); } /** * {@inheritDoc} */ @Override public void onChannelClosed(String remoteAddr) { handlerTable.remove(remoteAddr); option().serverOptions().forEach(serverOption -> { if (StringUtils.equals(serverOption.addr(), remoteAddr)) { // 连接监听 if (option().serverConnectionListener() != null) { option().serverConnectionListener().onClosed(serverOption); } if (serverOption.state() != ConnectState.SHUTDOWN) { serverOption.state(ConnectState.CLOSED); scheduledPoolExecutor.schedule(() -> doConnect(serverOption), getTimeoutSeconds(serverOption), TimeUnit.SECONDS); } } }); } /** * {@inheritDoc} */ @Override public void handleAuthRequest(String addr, InboundChannelHandler inboundChannelHandler) { log.info("Auth requested[{}], sending [auth {}]", addr, "*****"); for (ServerOption serverOption : option().serverOptions()) { String password = serverOption.password(); if (password == null) { password = option().defaultPassword(); } if (StringUtils.equals(addr, serverOption.addr())) { EslMessage response = inboundChannelHandler.sendSyncSingleLineCommand("auth " + password); log.debug("Auth response [{}]", response); if (response.getContentType().equals(EslHeaders.Value.COMMAND_REPLY)) { CommandResponse reply = new CommandResponse("auth " + password, response); serverOption.state(ConnectState.AUTHED); log.info("Auth response success={}, message=[{}]", reply.isOk(), reply.getReplyText()); if (!option().events().isEmpty()) { StringBuilder sb = new StringBuilder(); for (String event : option().events()) { sb.append(event).append(" "); } setEventSubscriptions(addr, "plain", sb.toString()); } } else { serverOption.state(ConnectState.AUTHED_FAILED); log.error("Bad auth response message [{}]", response); throw new IllegalStateException("Incorrect auth response"); } } } } /** * {@inheritDoc} */ @Override public void handleEslEvent(String addr, EslEvent event) { option().listeners().forEach(listener -> { long start = 0L; if (option().performance()) { start = System.currentTimeMillis(); } if (option().eventPerformance()) { long cost = 0L; if (start > 0L) { cost = start - (event.getEventDateTimestamp() / 1000); } else { cost = System.currentTimeMillis() - (event.getEventDateTimestamp() / 1000); } if (cost > option().eventPerformanceCostTime()) { log.warn("[event performance] received esl event diff time : {}ms, event is blocked.", cost); } } log.debug("Event addr[{}] received [{}]", addr, event); /* * Notify listeners in a different thread in order to: * - not to block the IO threads with potentially long-running listeners * - generally be defensive running other people's code * Use a different worker thread pool for async job results than for event driven * events to keep the latency as low as possible. */ if (StringUtils.equals(event.getEventName(), EslConstant.BACKGROUND_JOB)) { try { listener.backgroundJobResultReceived(addr, event); } catch (Throwable t) { log.error("Error caught notifying listener of job result [{}], remote address [{}]", event, addr, t); } } else { try { listener.eventReceived(addr, event); } catch (Throwable t) { log.error("Error caught notifying listener of event [{}], remote address [{}]", event, addr, t); } } if (option().performance()) { long cost = System.currentTimeMillis() - start; if (cost >= option().performanceCostTime()) { log.warn("[performance] handle esl event cost time : {}ms", cost); } } }); } /** * {@inheritDoc} */ @Override public void handleDisconnectNotice(String addr) { log.info("Disconnected[{}] ...", addr); } /** *

getAuthedHandler.

* * @param addr a {@link java.lang.String} object. * @return a {@link link.thingscloud.freeswitch.esl.inbound.handler.InboundChannelHandler} object. */ public InboundChannelHandler getAuthedHandler(String addr) { InboundChannelHandler handler = handlerTable.get(addr); if (handler == null) { throw new InboundClientException("not found inbound handler for addr : " + addr); } List serverOptions = option().serverOptions(); for (ServerOption serverOption : serverOptions) { if (StringUtils.equals(serverOption.addr(), addr)) { if (serverOption.state() != ConnectState.AUTHED) { throw new InboundClientException("inbound handler is not authed for addr : " + addr); } break; } } return handler; } private void addServerOptionListener() { option().serverOptionListener(new ServerOptionListener() { @Override public void onAdded(ServerOption serverOption) { if (serverOption.state() == ConnectState.INIT) { doConnect(serverOption); } } @Override public void onRemoved(ServerOption serverOption) { if (serverOption.state() == ConnectState.CONNECTED || serverOption.state() == ConnectState.AUTHED) { doClose(serverOption); } } }); } private void addEventListener() { log.info("add event listener ..."); option().eventListener(new EventListener() { @Override public void addEvents(List list) { StringBuilder sb = new StringBuilder(); for (String event : list) { sb.append(event).append(" "); } option().serverOptions().forEach(serverOption -> publicExecutor.execute(() -> setEventSubscriptions(serverOption.addr(), "plain", sb.toString()))); } @Override public void cancelEvents() { option().serverOptions().forEach(serverOption -> publicExecutor.execute(() -> cancelEventSubscriptions(serverOption.addr()))); } }); } private void doConnect(final ServerOption serverOption) { log.info("connect remote server [{}:{}] ...", serverOption.host(), serverOption.port()); serverOption.addConnectTimes(); serverOption.state(ConnectState.CONNECTING); bootstrap.connect(serverOption.host(), serverOption.port()).addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { serverOption.state(ConnectState.CONNECTED); log.info("connect remote server [{}:{}] success.", serverOption.host(), serverOption.port()); } else { serverOption.state(ConnectState.FAILED); log.warn("connect remote server [{}:{}] failed, will try again, cause : ", serverOption.host(), serverOption.port(), future.cause()); scheduledPoolExecutor.schedule(() -> doConnect(serverOption), getTimeoutSeconds(serverOption), TimeUnit.SECONDS); } }); } private void doClose(ServerOption serverOption) { log.info("doClose remote server [{}:{}] success.", serverOption.host(), serverOption.port()); serverOption.state(ConnectState.CLOSING); option().serverOptions().remove(serverOption); String remoteAddr = serverOption.addr(); InboundChannelHandler inboundChannelHandler = handlerTable.get(remoteAddr); if (inboundChannelHandler != null) { inboundChannelHandler.close().addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { log.info("close remote server [{}:{}] success.", serverOption.host(), serverOption.port()); } else { log.info("close remote server [{}:{}] failed, cause : ", serverOption.host(), serverOption.port(), future.cause()); } }); } } private int getTimeoutSeconds(ServerOption serverOption) { return serverOption.timeoutSeconds() == 0 ? option().defaultTimeoutSeconds() : serverOption.timeoutSeconds(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy