/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* WSO2 Inc. 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 org.apache.synapse.debug;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.debug.constants.SynapseDebugInterfaceConstants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Semaphore;
/**
* This is the main class that handles communication channels to the outside debugger. Mainly
* handles connection creation to receive and send debug commands and to send debug related events
* to the outside debugger. Channel creation happens asynchronously in a separate thread,
* but initialization of ESB server runtime ( mediation initializer ) is paused until a successful
* creation of communication channel.
*/
public class SynapseDebugInterface {
private int listernPortNumber;
private int sendPortNumber;
private ServerSocket listenSocket;
private ServerSocket sendSocket;
private PrintWriter sendSocketWriter;
private BufferedReader listenSocketReader;
private PrintWriter listenSocketWriter;
private static final Log log = LogFactory.getLog(SynapseDebugInterface.class);
private static SynapseDebugInterface debugInterfaceInstance = null;
private Semaphore runtimeSuspensionSem;
private static final int SOCKET_TIMEOUT_INTERVAL = 60 * 1000;
private volatile Exception uncaughtException;
/**
* Initializes the communication command and event channels asynchronously.
*
* @param listenPortParam command port number
* @param sendPortParam event port number
*/
public void init(final int listenPortParam, final int sendPortParam)
throws InterruptedException, IOException {
log.info(SynapseDebugInterfaceConstants.LISTEN_ON_PORTS + " : Command "
+ listenPortParam + " - Event " + sendPortParam);
this.runtimeSuspensionSem = new Semaphore(0);
Thread channelCreator = new Thread(new AsynchronousChannelCreator
(listenPortParam, sendPortParam, runtimeSuspensionSem));
channelCreator.start();
this.runtimeSuspensionSem.acquire();
if (uncaughtException != null) {
throw (IOException) uncaughtException;
}
}
public void createDebugChannels(final int listenPortParam, final int sendPortParam)
throws IOException {
this.listernPortNumber = listenPortParam;
this.sendPortNumber = sendPortParam;
this.listenSocket = new ServerSocket(listernPortNumber);
this.sendSocket = new ServerSocket(sendPortNumber);
this.listenSocket.setSoTimeout(SOCKET_TIMEOUT_INTERVAL);
this.sendSocket.setSoTimeout(SOCKET_TIMEOUT_INTERVAL);
Socket listenClientSocket = this.listenSocket.accept();
Socket sendClientSocket = this.sendSocket.accept();
log.info(SynapseDebugInterfaceConstants.CONNECTION_CREATED);
this.sendSocketWriter = new PrintWriter(sendClientSocket.getOutputStream());
this.listenSocketReader = new BufferedReader(
new InputStreamReader(listenClientSocket.getInputStream()));
this.listenSocketWriter = new PrintWriter(listenClientSocket.getOutputStream());
}
public static SynapseDebugInterface getInstance() {
if (debugInterfaceInstance == null) {
debugInterfaceInstance = new SynapseDebugInterface();
}
return debugInterfaceInstance;
}
/**
* Closes the communication and event channels
*/
public void closeConnection() {
try {
if (getOpenedPortListen() != null && !getOpenedPortListen().isClosed()) {
getOpenedPortListen().close();
}
if (getOpenedPortSend() != null && !getOpenedPortListen().isClosed()) {
getOpenedPortSend().close();
}
log.info(SynapseDebugInterfaceConstants.CONNECTION_CLOSED);
} catch (IOException e) {
log.error("Failed close communication channels to the external debugger", e);
}
}
public ServerSocket getOpenedPortListen() {
return listenSocket;
}
public ServerSocket getOpenedPortSend() {
return this.sendSocket;
}
public PrintWriter getPortSendWriter() {
return this.sendSocketWriter;
}
public BufferedReader getPortListenReader() {
return this.listenSocketReader;
}
public PrintWriter getPortListenWriter() {
return this.listenSocketWriter;
}
public void setUncaughtException(Exception ex) {
this.uncaughtException = ex;
}
class AsynchronousChannelCreator implements Runnable {
private int listenPortParam;
private int sendPortParam;
private Semaphore runtimeSuspendSem;
public AsynchronousChannelCreator(int listenPortParam, int sendPortParam,
Semaphore runtimeSuspendSem) {
this.listenPortParam = listenPortParam;
this.sendPortParam = sendPortParam;
this.runtimeSuspendSem = runtimeSuspendSem;
}
@Override
public void run() {
try {
createDebugChannels(listenPortParam, sendPortParam);
} catch (IOException ex) {
setUncaughtException(ex);
log.error("Failed create communication channels to the external debugger", ex);
} finally {
runtimeSuspendSem.release();
}
}
}
}