SockJS Java Client auto reconnect - spring-websocket

I am using SockJs Java client to connect with websocket running on different server. Every thing works fine like server publishes message and my Java client receives it,but if server is restarted then I am not able to receive any reply.But when I restart the client then again everything work ok. So I want to implement the re-connection logic with restarting the SockJs Java client. My code is as below:
#SpringBootApplication
public class Application {
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
private static Logger logger = Logger.getLogger(Application.class);
public static void main(String[] args) throws InterruptedException, ExecutionException {
SpringApplication.run(Application.class, args);
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
String url = "ws://{host}:{port}/hello";
ListenableFuture<StompSession> f = stompClient.connect(url, headers, new MyWebSocketHandler(), "localhost", 9090);
StompSession stompSession = f.get();
logger.info("Subscribing to greeting topic using session " + stompSession);
subscribeGreetings(stompSession);
}
public static void subscribeGreetings(StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe("/topic/greetings", new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
logger.info("Received greeting " + new String((byte[]) o));
}
});
}
}

Some of my java code, but is for a raw websocket, dont using STOMP.
Based on this javascript recconnecting implementation: https://github.com/joewalnes/reconnecting-websocket
Java code:
/**
*
* Problemas conocidos:
*
* Si se realiza la instancia dos veces consecutivas, se generan dos conexiones. Esto es debido a que el Executor
* crea dos tareas y no limita.
* Una posible solucion seria convertir esta API de reconexion en un Singleton, sin embargo hacer eso significa solo
* poder crear una coneccion.
* Una forma de solucionar esto es crear una fabrica de websockets. Y por otro lado tener la API como un singleton.
*
* Para reproducir el problema:
*
* ReconnectWebsocket ws = new ReconnectWebsocket(new LogicWsExternal() {
#Override
public void onOpen(Session session) {
} // This is myPersonalEndPoint
},uri, settings
);
ws = new ReconnectWebsocket(new LogicWsExternal() {
#Override
public void onOpen(Session session) {
} // This is myPersonalEndPoint
},uri, settings
);
// .. dos conexiones...
*
*
*
* #author gas
*
*/
public class ReconnectWebsocket implements ReconnectObserver{
private final Logger LOG = LoggerFactory.getLogger(ReconnectWebsocket.class);
//http://stackoverflow.com/questions/5762491/how-to-print-color-in-console-using-system-out-println
public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_BLUE = "\u001B[34m";
public static final String ANSI_WHITE = "\u001B[37m";
// private static ReconnectWebsocketTest instance = null;
Boolean debug;
Boolean automaticOpen;
Integer reconnectInterval;
Integer maxReconnectionInterval;
Float reconnectDecay;
Integer timeoutInterval;
Integer maxConnectAttempts;
String binaryType;
// These should be treated as read-only properties
/** The URL as resolved by the constructor. This is always an absolute URL. Reas only. */
URI path;
/** The number of attempted reconnects since starting, or the last successful connection. Read only. */
int connectAttemptsCount;
/**
* The current state of the connection.
* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
* Read only.
*/
WebSocketStates readyState = WebSocketStates.CLOSED;
/**
* A string indicating the name of the sub-protocol the server selected; this will be one of
* the strings specified in the protocols parameter when creating the WebSocket object.
* Read only.
*/
// TODO
//this.protocol = null;
// Private state variables
//ReconnectWebsocket self = this;
Session session; // In Javascript implementation is ws variable.
boolean forcedClose = false;
boolean timeOutFlag = false;
Future<Boolean> timerReconnectionFuture; // Usado para controlar el timer para las reconexnes.
//private ReconnectObservable observable;
LogicWsExternal logicExternal;
ClientManager client;
LogicWs wsLogic;
static ScheduledExecutorService executor;
static ScheduledExecutorService executor2;
Future openFuture;
AtomicBoolean openFlag;
/*
*
* Tyrus estates:
* org.glassfish.tyrus.core.TyrusWebSocket.State
*
* */
public static enum WebSocketStates { // Tyrus:
CONNECTING("CONNECTING",0) // NEW
,OPEN("OPEN",1) // CONNECTED
,CLOSING("CLOSING",2) // CLOSING
,CLOSED("CLOSED",3) // CLOSED
;
String desc;
Integer statusInt;
WebSocketStates(String desc,Integer statusInt){
this.desc=desc;
this.statusInt=statusInt;
}
public String getDescription() {
return desc;
}
public Integer getStatusInt() {
return statusInt;
}
public void printDescription(){
System.out.println("PrinterStatus: "+desc);
}
}
public ReconnectWebsocket(URI path) throws DeploymentException, IOException {
this(new LogicWsExternal(){
#Override
public void onOpen(Session session) { }},path, null);
}
// Consturctor whith Only package visivility
public ReconnectWebsocket(LogicWsExternal logicWsExernal, URI path) throws DeploymentException, IOException {
this(logicWsExernal,path, null);
}
public ReconnectWebsocket(LogicWsExternal logicWsExternal, URI path, ReconnectSettings settings) {
// Default setting
// Overwrite and define settings with options if they exist.
/** Wheter this instance should log debug mesages. */
this.debug = settings.getDebug()!=null ? settings.getDebug() : true;
/** Wheter or not the websocket should attempt to connect immediately upon instantiation. */
this.automaticOpen = settings.getAutomaticOpen() !=null ? settings.getAutomaticOpen() : true;
/** The number of milliseconds to delay before attempting to reconnect. */
this.reconnectInterval = settings.getReconnectInterval()!=null ? settings.getReconnectInterval() : 1000;
/** The maximum number of milliseconds to delay a reconnection attempt. Timeout to reconnect */
this.maxReconnectionInterval = settings.getMaxReconnectionInterval()!=null ? settings.getMaxReconnectionInterval() : 10000;
/** The rate of increase of the reconnect delay. Allows reconnect attemps to back off when problems persist. */
this.reconnectDecay = settings.getReconnectDecay()!=null ? settings.getReconnectDecay() : (float) 1.3;
/** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying */
this.timeoutInterval = settings.getTimeoutInterval()!=null ? settings.getTimeoutInterval() : 5000;
/** The number of connection attempts to make before to stop. Unlimited if value is zero.
**/
this.maxConnectAttempts = settings.getMaxConnectAttempts()!=null ? settings.getMaxConnectAttempts() : 0;
/** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
this.binaryType = settings.getBinaryType()!=null ? settings.getBinaryType() : "blob";
//settings.put("idStateEvenbusChannel", "false");
//settings.put("idStateEvenbusChannel", "false");
// These should be treated as read-only properties
/** The URL as resolved by the constructor. This is always an absolute URL. Reas only. */
this.path = path;
/** The number of attempted reconnects since starting, or the last successful connection. Read only. */
this.connectAttemptsCount = 0;
/**
* The current state of the connection.
* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
* Read only.
*/
this.readyState = WebSocketStates.CLOSED;
/**
* A string indicating the name of the sub-protocol the server selected; this will be one of
* the strings specified in the protocols parameter when creating the WebSocket object.
* Read only.
*/
// TODO
// "has a" rather than "is a" observable
//observable = new ReconnectObservable();
this.logicExternal = logicWsExternal;
wsLogic = new LogicWs();
//client = ClientManager.createClient();//GLiszli lient by default
// Java 7 cient.
client = ClientManager.createClient(JdkClientContainer.class.getName());
wsLogic.addObserver(this);
// By default initialize the executors.
executor = Executors.newScheduledThreadPool(1);
executor2 = Executors.newScheduledThreadPool(1);
openFlag = new AtomicBoolean(true);
// Wheher or not to create a websocket upon instantiation
if (this.automaticOpen) {
//this.open();
this.open();
}
}
public void open() {
if (readyState == WebSocketStates.CONNECTING || readyState == WebSocketStates.OPEN ) {
return;
}
if (executor.isShutdown()) {
executor = Executors.newScheduledThreadPool(1);
}
if (executor2.isShutdown()) {
/*
* Este poolthread se apaga cuando se manda a llamar la funcion close() de la API.
* El apagado se realiza porque se considera que ya no se va o volver a conectar.
*/
executor2 = Executors.newScheduledThreadPool(1);
}
/*
* Resetear variables
*/
AtomicInteger counter = new AtomicInteger(0);
connectAttemptsCount = 0;
readyState = WebSocketStates.CONNECTING;
// Ejecutar funciones en metodo OnConnecting
//String reconnectReason = e.getMessage();
//self.update(new InternalMessageWs(WsEventType.ONCONNECTING,new OnConnectingEvent(reconnectReason)));
update(new InternalMessageWs(WsEventType.ONCONNECTING,new OnConnectingEvent("First Connect")));
Runnable openRun = () -> {
do{
if (debug) {
System.out.println("DEBUG: ReconnectingWebSocket attempt-connect# "+(connectAttemptsCount+1)+" of "+(maxConnectAttempts==0?"infinite":maxConnectAttempts)+" URI="+path.getPath());
}
Callable<Session> task1 = new Callable<Session>() {
#Override
public Session call() throws Exception {
// Avizar al API que se esta intentando realizar una reconexión. Patron productor-consumidor.
// TODO Deberia de ser un hilo?? ESto ya que podria se r que algo en el metodo externo sea de tipo bloqueante.
watcherReconnectionTry();
Session session = client.connectToServer(wsLogic,path);
//self.session = client.connectToServer(wsLogic,self.path);
//System.out.println("ReconnectWebsocket:: client.connectToServer(...) is null:"+(session==null?"true":"false"));
//System.out.println("ReconnectWebsocket:: client.connectToServer(...) is open:"+session.isOpen());
return session;
}
};
Future<Session> future = executor.submit(task1);
try {
// Tiempo de espera antes de interrumpir volver a intentarlo.
//Session s = future.get(self.timeoutInterval,TimeUnit.MILLISECONDS);
//Session s = future.get(30,TimeUnit.SECONDS);
//return s;
//self.session = future.get(30,TimeUnit.SECONDS);
session = future.get(timeoutInterval,TimeUnit.MILLISECONDS);
break;
} catch (InterruptedException | ExecutionException | TimeoutException e) {
// TODO Auto-generated catch block
//e.printStackTrace(); //For debug only
// Calculate Back off time.
/*
*
*/
float timeout = (float) (reconnectInterval * Math.pow(reconnectDecay,connectAttemptsCount));
connectAttemptsCount++;
if (maxConnectAttempts > 0 && ( connectAttemptsCount >= maxConnectAttempts )) {
break;
}
int maxTimeReconnect = (int) (timeout > maxReconnectionInterval ? maxReconnectionInterval : timeout);
counter.set(maxTimeReconnect/1000);
Callable<Boolean> timerReconnection = new Callable<Boolean>() {
#Override
public Boolean call() {
System.out.println("counter.get()="+counter.get());
while(counter.get() >= 0) {
System.out.println("Time next reconection: "+counter.get()+" seconds");
System.out.println("ThreadId: "+Thread.currentThread().getId() );
// Avizar a la API el tiempo para la sig. reconexión.. Patron productor-consumidor.
// TODO Deberia de ser un hilo?? ESto ya que podria se r que algo en el metodo externo sea de tipo bloqueante.
watcherTimeLeft(counter.get());
if (counter.get() == 0 && debug ) {
System.out.println("DEBUG: ReconnectingWebSocket connection-timeout: "+path.getPath());
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
counter.decrementAndGet();
}
return false;
}
};
/*
* scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS)
*
* 0 -> initialDelay, this time is includen in the backoff algorithm calc, then this value is zero.
* timeout -> the delay between the termination of one execution and the commencement of the next
*/
timerReconnectionFuture = executor.submit(timerReconnection);
try {
Boolean delayTime = timerReconnectionFuture.get();
} catch (InterruptedException | ExecutionException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}while (!Thread.currentThread().isInterrupted()); //END while
//while (openFlag.get()); //END while
};// END Runnable
openFuture = executor2.submit(openRun);
}//
public void send(String str) {
// Convertir el texto a objeto Message para enviarlo.
if (readyState == WebSocketStates.OPEN) {
if (debug) {
LOG.debug("Sending to URL:\"{}\", Data:\n\"{}\" ",path.getPath(),JsonWriter.formatJson(str));
//System.out.println("DEBUG: ReconnectingWebSocket sending to "+path.getPath()+": "+str);
}
//session.getBasicRemote().sendText(data);
try {
session.getBasicRemote().sendText(str);
} catch (IOException e) {
LOG.error("Sending to URL:\"{}\", Data:\"{}\" {}",path.getPath(),str,e);
//e.printStackTrace();
}
}
}
/**
*
* #param j objeto Json a enviar por Websocket.
* #throws EncodeException
* #throws IOException
*/
public void send(Json j) {
//System.out.println("ReconnectWebsocket:: send(Json INI)");
send(new Message(j));
//System.out.println("ReconnectWebsocket:: send(Json END)");
}
/**
* Transmits data to the server over Websocket connection.
*
* #param data a text string, ArrayBuffer or Blob to send to the server.
* #throws IOException
* #throws EncodeException
*/
public void send(Message data) {
//System.out.println("ReconnectWebsocket:: send(Message INI)");
if (readyState == WebSocketStates.OPEN) {
if (debug) {
System.out.println("DEBUG: ReconnectingWebSocket send "+path.getPath()+": "+data);
}
//System.out.println("ReconnectWebSocket::send(Message msg - Before)" );
//System.out.println("ReconnectWebSocket::send(Message msg - Before - session is null="+(session==null?"true":"false" ));
//System.out.println("ReconnectWebSocket::send(Message msg - Before - session is open="+session.isOpen());
//session.getBasicRemote().sendText(data);
try {
session.getBasicRemote().sendObject(data);
LOG.debug("Sending to URL:\"{}\", Data:\"{}\" ",path.getPath(),data);
//System.out.println("REconnectWebSocket::send(Message msg - After)" );
} catch (IOException | EncodeException e) {
//e.printStackTrace();
LOG.error("Sending to URL:\"{}\", Data:\"{}\" {}",path.getPath(),data,e);
}
}
// Deberia de detenerse la reconeccion en estos casos?, es decir detener despues de intentar enviar una
// cadena de texto pero que ha fallado.
//System.out.println("ReconnectWebsocket:: send(Message END)");
}
public void close(String reason) {
close(CloseReason.CloseCodes.NORMAL_CLOSURE,reason);
}
public void close() {
close(CloseReason.CloseCodes.NORMAL_CLOSURE,null);
}
public void close(CloseReason.CloseCodes code) {
close(code,null);
}
/** Closes the Websocket connection or connection attempt, if any.
* If the connection is already CLOSED, this method does nothing.
*/
public void close(CloseReason.CloseCodes code, String reason) {
if (readyState == WebSocketStates.CLOSED) {
return;
}
CloseReason closeReason;
forcedClose = true;
/*
* Status code: 1000
* Name: CLOSE_NORMAL
* Description: The connection successfully completed whatever purpose for
* which it was created.
* https://developer.mozilla.org/es/docs/Web/API/CloseEvent
*/
// Default CLOSE_NORMAL code
if (code==null) {
closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE,"ReconnectingWebSocket STOP");
} else if (reason!=null) {
closeReason = new CloseReason(code,reason);
} else {
closeReason = new CloseReason(code,"ReconnectingWebSocket STOP");
}
if ( (readyState == WebSocketStates.OPEN || readyState == WebSocketStates.CONNECTING) ) {
// Change readyState status:
readyState = WebSocketStates.CLOSED;
if (session==null) {
/*
* readyState == WebSocketStates.CONNECTING && session == null
*
* This ocurr when the server is off and the client is in a loop trying to connect.
*/
timerReconnectionFuture.cancel(true);
//openFuture.cancel(true);
} else {
/*
* readyState == WebSocketStates.OPEN && session != null
* or
* readyState == WebSocketStates.CONNECTING && session != null
*/
try { // Permanent close. Called via the Close method.
if (session.isOpen()) {
/*
* Session is previously closed when has connected at less one time, after the server shutdown
* and the reconnection beging. During the reconnection if you try to close (forced close)
* then session.close will thorwn a error.
* To fix we have verificate if the session is closed.
*/
session.close(closeReason);
}
executor.shutdown();
executor2.shutdown();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
LOG.error("Error cerrando sesion. {}",e);
}
}
//openFuture.cancel(true);
}
}
/**
* Additional public API method to refresh the connection if still is open.
* After close, the websocket will try to reconnect.
* For example, if the app suspects bad data / missed heart beats, it can try to refresh.
*/
public void refresh() {
if (readyState == WebSocketStates.OPEN) {
try {
session.close(new CloseReason(CloseCodes.SERVICE_RESTART, "refresh!!"));
} catch (IOException e) {
//e.printStackTrace();
LOG.error("Error cerrando sesion. {}",e);
}
} else {
// Stop timer of reconnection.
if (readyState == WebSocketStates.CONNECTING) {
//timerReconnectionFuture.cancel(true);
close(CloseCodes.SERVICE_RESTART, "refresh!!");
// Reset variables.
connectAttemptsCount = 0;
open();
}
}
}
public Session getSession(){
return session;
}
public WebSocketStates getReadyState() {
return readyState;
}
/**
* El observador de los cambios en el clente Websocket interno.
*/
#Override
public void update(InternalMessageWs msg) {
switch (msg.getType()) {
case ONOPEN:
// Cambiar estado de la conexión
readyState = WebSocketStates.OPEN;
if (debug ) {
System.out.println("DEBUG: ReconnectingWebSocket onOpen: "+path.getPath());
}
// Ejecutar las funciones onOPen que el usuario ha definido.
logicExternal.onOpen( ((OnOpenEvent)msg.getEvent()).getSession() );
break;
case ONMESSAGE:
if (debug) {
System.out.println("DEBUG: ReconnectingWebSocket onMessage: "+path.getPath());
}
OnMessageEvent evtMsg = (OnMessageEvent)msg.getEvent();
// Ejecutar las funciones OnMessage que el usuario ha definido.
logicExternal.onMessage(evtMsg.getSession(),evtMsg.getMessage());
break;
case ONCLOSE:
if (debug ) {
System.out.println("DEBUG: ReconnectingWebSocket onClose: "+path.getPath()+" forcedClose="+forcedClose);
}
// Cambiar estado de la conexión
readyState = WebSocketStates.CLOSED;
OnCloseEvent evtClose = (OnCloseEvent)msg.getEvent();
// Ejecutar las funciones OnClose que el usuario ha definido.
logicExternal.onClose(evtClose.getSession(),evtClose.getReason());
/*
* Determinar si se debe vlver a conectar o no.
* Si forcedClose = true, entonces detener.
* Si forcedClose = false, entonces reconectar.
*/
if (!forcedClose) {
this.open();
}
else{
if (debug ) {
System.out.println("DEBUG: ReconnectingWebSocket STOP Reconnectiing: "+path.getPath());
}
forcedClose = false;
}
break;
case ONERROR:
if (debug ) {
System.out.println("DEBUG: ReconnectingWebSocket onError: "+path.getPath());
}
// Cambiar estado de la conexión
readyState = WebSocketStates.CLOSED;
OnErrorEvent evtError = (OnErrorEvent)msg.getEvent();
// Ejecutar las funciones OnError que el usuario ha definido.
logicExternal.onError(evtError.getSession(),evtError.getT());
// Volver a iniciar secuencia de conectar.
this.open();
// Algunos prfieren cerrar la conexion.
//this.close(CloseCodes.CLOSED_ABNORMALLY,evtError.getT().getMessage());
break;
case ONCONNECTING:
if (debug ) {
System.out.println("DEBUG: ReconnectingWebSocket onConnecting: "+path.getPath());
}
OnConnectingEvent evtConnecting = (OnConnectingEvent)msg.getEvent();
// Ejecutar las funciones OnConnecting que el usuario ha definido.
logicExternal.onConnecting(evtConnecting.getReason());
break;
default:
break;
}
}
#Override
public void watcherReconnectionTry() {
logicExternal.watcherReconnectionTry();
}
#Override
public void watcherTimeLeft(int timeLeft) {
logicExternal.watcherTimeLeft(timeLeft);
}
}
One interface:
public interface ReconnectObserver {
public void update(InternalMessageWs msg);
public void watcherReconnectionTry();
public void watcherTimeLeft(int timeLeft);
}
ReconnectObservable class:
import java.util.ArrayList;
public class ReconnectObservable implements ReconnectSubject {
private ArrayList<ReconnectObserver> observers;
public ReconnectObservable() {
observers = new ArrayList<ReconnectObserver>();
}
#Override
public void addObserver(ReconnectObserver observer) {
observers.add(observer);
}
#Override
public void notifyObservers(InternalMessageWs msg) {
for(ReconnectObserver observer : observers) {
observer.update(msg);
}
}
}
ReconnectSubject interface:
public interface ReconnectSubject {
public void addObserver(ReconnectObserver observer);
//public void notifyObservers();
public void notifyObservers(InternalMessageWs msg);
}
InternalMessageWs class:
import javax.websocket.CloseReason;
import javax.websocket.Session;
public class InternalMessageWs {
WsEventType type;
Object event;
InternalMessageWs(WsEventType type) {
this.type = type;
this.event = null;
}
InternalMessageWs(WsEventType type, Object event) {
this.type = type;
this.event=event;
}
public WsEventType getType() {
return type;
}
public void setType(WsEventType type) {
this.type = type;
}
public Object getEvent() {
return event;
}
public void setEvent(Object event) {
this.event = event;
}
}
class OnOpenEvent {
Session session;
public OnOpenEvent(Session session) {
this.session = session;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
class OnMessageEvent {
Message message;
Session session;
public OnMessageEvent(Session session, Message message) {
this.message = message;
this.session = session;
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
class OnCloseEvent {
Session session;
CloseReason reason;
public OnCloseEvent(Session session, CloseReason reason) {
this.session = session;
this.reason = reason;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public CloseReason getReason() {
return reason;
}
public void setReason(CloseReason reason) {
this.reason = reason;
}
}
class OnErrorEvent {
Session session;
Throwable t;
public OnErrorEvent(Session session,Throwable t) {
this.t = t;
this.session=session;
}
public Throwable getT() {
return t;
}
public void setT(Throwable t) {
this.t = t;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
class OnConnectingEvent {
String reason;
public OnConnectingEvent(String reason) {
this.reason = reason;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
enum WsEventType {
ONOPEN,ONMESSAGE,ONCLOSE,ONERROR,
ONCONNECTING // Using in Reconnecting state of the Websocket client.
}
You need use Java 8 JDK because im usign callables, etc.

You can implement a proxy pattern. I do a reconnect using this Java pattern.
But my implementation is for a raw Websocket java client.
A proxy pattern involve the funcionality of original object. You need add a reconnect() and onReconnect() methods to the cicle life of websocket. Websocket only have onError, onConnect, OnMessage, OnClose methods.
I will implement a recconnector for a SockJS Java client, then I will post the code if you want.

Related

AWS transcribe Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain

I am running a java program that uses AWS TranscribeStreaming. I have created(from AWS console) and downloaded AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY then set the environment variables.
When I run the program I am getting error message:
Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain.
I don't why but this error was also occurring while using Google SDK for speech recognition.
here is the code I am trying to run.
public class TranscribeStreamingDemoApp {
private static final Region REGION = Region.US_WEST_2;
private static TranscribeStreamingAsyncClient client;
public static void main(String args[]) throws URISyntaxException, ExecutionException, InterruptedException, LineUnavailableException {
client = TranscribeStreamingAsyncClient.builder()
.credentialsProvider(getCredentials())
.region(REGION)
.build();
CompletableFuture<Void> result = client.startStreamTranscription(getRequest(16_000),
new AudioStreamPublisher(getStreamFromMic()),
getResponseHandler());
result.get();
client.close();
}
private static InputStream getStreamFromMic() throws LineUnavailableException {
// Signed PCM AudioFormat with 16kHz, 16 bit sample size, mono
int sampleRate = 16000;
AudioFormat format = new AudioFormat(sampleRate, 16, 1, true, false);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
InputStream audioStream = new AudioInputStream(line);
return audioStream;
}
private static AwsCredentialsProvider getCredentials() {
return DefaultCredentialsProvider.create();
}
private static StartStreamTranscriptionRequest getRequest(Integer mediaSampleRateHertz) {
return StartStreamTranscriptionRequest.builder()
.languageCode(LanguageCode.FR_FR.toString())
.mediaEncoding(MediaEncoding.PCM)
.mediaSampleRateHertz(mediaSampleRateHertz)
.build();
}
private static StartStreamTranscriptionResponseHandler getResponseHandler() {
return StartStreamTranscriptionResponseHandler.builder()
.onResponse(r -> {
System.out.println("Received Initial response");
})
.onError(e -> {
System.out.println(e.getMessage());
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
System.out.println("Error Occurred: " + sw.toString());
})
.onComplete(() -> {
System.out.println("=== All records stream successfully ===");
})
.subscriber(event -> {
List<software.amazon.awssdk.services.transcribestreaming.model.Result> results = ((TranscriptEvent) event).transcript().results();
if (results.size() > 0) {
if (!results.get(0).alternatives().get(0).transcript().isEmpty()) {
System.out.println(results.get(0).alternatives().get(0).transcript());
}
}
})
.build();
}
private InputStream getStreamFromFile(String audioFileName) {
try {
File inputFile = new File(getClass().getClassLoader().getResource(audioFileName).getFile());
InputStream audioStream = new FileInputStream(inputFile);
return audioStream;
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
private static class AudioStreamPublisher implements Publisher<AudioStream> {
private final InputStream inputStream;
private static Subscription currentSubscription;
private AudioStreamPublisher(InputStream inputStream) {
this.inputStream = inputStream;
}
#Override
public void subscribe(Subscriber<? super AudioStream> s) {
if (this.currentSubscription == null) {
this.currentSubscription = new SubscriptionImpl(s, inputStream);
} else {
this.currentSubscription.cancel();
this.currentSubscription = new SubscriptionImpl(s, inputStream);
}
s.onSubscribe(currentSubscription);
}
}
public static class SubscriptionImpl implements Subscription {
private static final int CHUNK_SIZE_IN_BYTES = 1024 * 1;
private final Subscriber<? super AudioStream> subscriber;
private final InputStream inputStream;
private ExecutorService executor = Executors.newFixedThreadPool(1);
private AtomicLong demand = new AtomicLong(0);
SubscriptionImpl(Subscriber<? super AudioStream> s, InputStream inputStream) {
this.subscriber = s;
this.inputStream = inputStream;
}
#Override
public void request(long n) {
if (n <= 0) {
subscriber.onError(new IllegalArgumentException("Demand must be positive"));
}
demand.getAndAdd(n);
executor.submit(() -> {
try {
do {
ByteBuffer audioBuffer = getNextEvent();
if (audioBuffer.remaining() > 0) {
AudioEvent audioEvent = audioEventFromBuffer(audioBuffer);
subscriber.onNext(audioEvent);
} else {
subscriber.onComplete();
break;
}
} while (demand.decrementAndGet() > 0);
} catch (Exception e) {
subscriber.onError(e);
}
});
}
#Override
public void cancel() {
executor.shutdown();
}
private ByteBuffer getNextEvent() {
ByteBuffer audioBuffer = null;
byte[] audioBytes = new byte[CHUNK_SIZE_IN_BYTES];
int len = 0;
try {
len = inputStream.read(audioBytes);
if (len <= 0) {
audioBuffer = ByteBuffer.allocate(0);
} else {
audioBuffer = ByteBuffer.wrap(audioBytes, 0, len);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return audioBuffer;
}
private AudioEvent audioEventFromBuffer(ByteBuffer bb) {
return AudioEvent.builder()
.audioChunk(SdkBytes.fromByteBuffer(bb))
.build();
}
}
}
Finally, I solved the problem, the documentation specifies that:
AWS credentials provider chain that looks for credentials in this order:
1.Java System Properties - aws.accessKeyId and aws.secretAccessKey
2.Environment Variables - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
3.Credential profiles file at the default location (~/.aws/credentials) shared by all AWS SDKs and the AWS CLI
Since setting up through environment variables didn't work, I opted to set credentials with Java system properties and it works!
Doc ref: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html
System.setProperty("aws.accessKeyId", "**************");
System.setProperty("aws.secretAccessKey", "**************");

401:Authentication credentials were invalid - Invalid or expired token. code - 89

This is the code and I am recieving the error 401: Authentication Error
public class Server {
// initialize socket and input stream
private Socket socket = null;
private ServerSocket server = null;
private DataInputStream in = null;
public void tweet() throws TwitterException {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDaemonEnabled(true).setOAuthConsumerKey("......")
.setOAuthConsumerSecret("......")
.setOAuthAccessToken("......")
.setOAuthAccessTokenSecret(".....");
TwitterFactory tf = new TwitterFactory();
twitter4j.Twitter twitter = tf.getInstance();
List status = twitter.getHomeTimeline();
for (Status st : status) {
System.out.println(st.getUser().getName() + "---- Tweets----" + st.getText());
}
}
// constructor with port
public Server(int port) throws TwitterException {
// starts server and waits for a connection
try {
server = new ServerSocket(port);
System.out.println("Server started");
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted");
// takes input from the client socket
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
String line = "";
// reads message from client until "Over" is sent
while (!line.equals("Over")) {
try {
line = in.readUTF();
System.out.println(line);
if (line.equalsIgnoreCase("Data")) {
tweet();
}
} catch (IOException i) {
System.out.println(i);
}
}
System.out.println("Closing connection");
// close connection
socket.close();
in.close();
} catch (IOException i) {
System.out.println(i);
}
}
public static void main(String args[]) throws TwitterException {
Server server = new Server(5000);
}
}
Please make sure that the tokens are valid.
Then, you could try enabling system proxies like so:
System.setProperty("java.net.useSystemProxies", "true");

Blackberry App is working on wi-fi but not on Packet Data/GPRS

I have created Blackberry Application which is working perfectly fine on wi-fi. But when I turn off wi-fi and use Mobile Network then I am not able to run application. It shows No Internet connection available.
I had written these lines.
if (DeviceInfo.isSimulator())
{
deviceinfo = deviceinfo.concat(";deviceside=true");
}
Here is My Http Connection Class
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
public class HttpConn
{
public String jsonresponse (String url)
{
String response = null;
HttpConnection httpConnection = null;
InputStream inStream = null;
int code;
StringBuffer stringBuffer = new StringBuffer();
String deviceinfo=url;
try
{
if (DeviceInfo.isSimulator())
{
deviceinfo = deviceinfo.concat(";deviceside=true");
}
/* else if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
{
deviceinfo = ";interface=wifi";
}*/
else if ( (RadioInfo.getActiveWAFs() & RadioInfo.WAF_WLAN) != 0 && RadioInfo.getSignalLevel( RadioInfo.WAF_WLAN ) != RadioInfo.LEVEL_NO_COVERAGE )
{
deviceinfo = deviceinfo.concat(";interface=wifi");
}
// else if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
// {
// deviceinfo = ";interface=wifi";//
// }
if ( (RadioInfo.getActiveWAFs() & RadioInfo.WAF_WLAN) != 0 && RadioInfo.getSignalLevel( RadioInfo.WAF_WLAN ) != RadioInfo.LEVEL_NO_COVERAGE )
{
deviceinfo = deviceinfo.concat(";interface=wifi");
}
// else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT)
// {
// String carrierUid = getCarrierBIBSUid();
//
// if (carrierUid == null)
// {
// // Has carrier coverage, but not BIBS. So use the carrier's TCP
// // network
//
// deviceinfo = ";deviceside=true";
//
// }
// else
// {
// // otherwise, use the Uid to construct a valid carrier BIBS
// // request
//
// deviceinfo = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
//
// }
// }
//
// // Check for an MDS connection instead (BlackBerry Enterprise Server)
// else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS)
// {
//
// deviceinfo = ";deviceside=false";
//
// }
//
// // If there is no connection available abort to avoid hassling the user
// // unnecssarily.
// else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE)
// {
// deviceinfo = "none";
//
//
// }
// else
// {
// deviceinfo=";deviceside=true";
// }
//if(CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_DIRECT,RadioInfo.WAF_WLAN,false))
// httpConnection = (HttpConnection) Connector.open(url+";interface=wifi", Connector.READ);
httpConnection = (HttpConnection) Connector.open(deviceinfo, Connector.READ);
httpConnection.setRequestMethod(HttpConnection.GET);
code = httpConnection.getResponseCode();
if(code == HttpConnection.HTTP_OK)
{
inStream=httpConnection.openInputStream();
int c;
while((c=inStream.read())!=-1)
{
stringBuffer.append((char)c);
}
response=stringBuffer.toString();
System.out.println("Response Getting from Server is ================" + response);
}
// Is the carrier network the only way to connect?
// In theory, all bases are covered by now so this shouldn't be reachable.But hey, just in case ...
else
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.inform("Connection error");
}
});
}
// return deviceinfo;
}
catch (Exception e)
{
System.out.println("caught exception in jsonResponse method"+e.getMessage());
}
finally
{
// if (outputStream != null)
// {
// outputStream.close();
// }
if (inStream != null)
{
try
{
inStream.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (httpConnection != null )
{
try
{
httpConnection.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return response;
}
// private synchronized static String getCarrierBIBSUid() {
// ServiceRecord[] records = ServiceBook.getSB().getRecords();
// int currentRecord;
//
// for (currentRecord = 0; currentRecord < records.length; currentRecord++) {
// if (records[currentRecord].getCid().toLowerCase().equals("ippp")) {
// if (records[currentRecord].getName().toLowerCase()
// .indexOf("bibs") >= 0) {
// return records[currentRecord].getUid();
// }
// }
// }
//
// return null;
// }
}
Actually solution is found.
java.io.IOException:APN is not specified . what APN i have to set manually to device for Wi-Fi network?
http://m2m.icpdas.com/download/gtm-201_modem/manual/gprs_apn.pdf
http://www.faqspedia.com/list-of-all-indian-mobile-operators-access-point-names
Refer this link.
Actually we have to set APN manually. We get it from the Provider.
I hope someone get solution from my this answer. Thanks a lot for your support.

IOException Radio is off, and Out of memory, on BlackBerry

I am performing HttpConnection in my RIM Blackberry application. I am using wi-fi connection. While performing HttpConnection sometimes it is returning data, sometimes it is giving
java.io.IOException Radio is off
and
java.io.IOException Out of memory
errors. I really do not understand what is the issue exactly. I am posting here my code snippets:
public static String getRemoteData(String url) throws ConnectionNotFoundException{
StringBuffer stringBuff=new StringBuffer();
try {
HttpConnection fconImg = (HttpConnection) Connector.open(url+ NetworkUtils.getConnectionString());
InputStream input = fconImg.openInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int k = 0;
while ((k = input.read()) != -1) {
baos.write(k);
}
byte[] byteArray = baos.toByteArray();
String s = new String(byteArray);
stringBuff.append(s.trim());
return stringBuff.toString();
} catch (Exception e) {
stringBuff.append("Exception : "+e.toString());
return stringBuff.toString();
}
}
And below is my NetworkUtils Class.
import net.rim.device.api.servicebook.ServiceBook;
import net.rim.device.api.servicebook.ServiceRecord;
import net.rim.device.api.system.CoverageInfo;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.system.WLANInfo;
public class NetworkUtils {
private static final String COVERAGE_CARRIER = "Carrier full Coverage";
private static final String COVERAGE_MDS = "BES coverage";
private static final String COVERAGE_NONE = "No coverage";
private static final String NOT_SUPPORTED_WAF = "Not supported by the device";
public static String logM;
/**
* Access the net.rim.device.api.system.DeviceInfo class in order to
* understand if the running system is a simulator or a real device.
* #return true if the current application is running on a Blackberry
* simulator, false otherwise
*/
public static boolean isSimulator() {
return DeviceInfo.isSimulator();
}
/**
* Give the information about the presence o a wifi bearer on the device
* #return true if the wifi communication interface bearer is supported by
* the device, false otherwise
*/
protected static boolean isWifiAvailable() {
// Log.info("Checking WIFI Availability");
boolean isWifiEnabled;
if (RadioInfo.areWAFsSupported(RadioInfo.WAF_WLAN)) {
// Log.info("WIFI Supported");
isWifiEnabled = true;
} else {
// Log.info("WIFI NOT Supported");
isWifiEnabled = false;
}
return isWifiEnabled;
}
/**
* Give information about the presence of active wifi connections.
* #return true if the device is connected to a wifi network with its wifi
* bearer, false otherwise
*/
protected static boolean isWifiActive() {
int active = RadioInfo.getActiveWAFs();
int wifi = RadioInfo.WAF_WLAN;
return active >= wifi;
}
protected static boolean isWapGprsDataBearerOffline() {
return RadioInfo.getState()==RadioInfo.STATE_OFF ||
RadioInfo.getSignalLevel() == RadioInfo.LEVEL_NO_COVERAGE;
}
public static String getNetworkCoverageReport() {
StringBuffer sb = new StringBuffer();
sb.append("\n*********************************************************");
sb.append("\nWireless Access Families:");
sb.append("\n3GPP: " + getNetworkCoverage(RadioInfo.WAF_3GPP));
sb.append("\nCDMA: " + getNetworkCoverage(RadioInfo.WAF_CDMA));
sb.append("\nWLAN: " + getNetworkCoverage(RadioInfo.WAF_WLAN));
sb.append("\nCDMA: " + getNetworkCoverage(RadioInfo.NETWORK_CDMA));
sb.append("\nBands:");
sb.append("\nCDMA_800: " + getNetworkCoverage(RadioInfo.BAND_CDMA_800));
sb.append("\nCDMA_1900: " + getNetworkCoverage(RadioInfo.BAND_CDMA_1900));
sb.append("\nNetworks:");
sb.append("\n802_11: " + getNetworkCoverage(RadioInfo.NETWORK_802_11));
sb.append("\nGPRS: " + getNetworkCoverage(RadioInfo.NETWORK_GPRS));
sb.append("\nNetwork services:");
sb.append("\nVOICE: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_VOICE));
sb.append("\nUMTS: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_UMTS));
sb.append("\nEDGE: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_EDGE));
sb.append("\n*********************************************************");
return sb.toString();
}
private static String getNetworkCoverage(int networkType) {
if (RadioInfo.areWAFsSupported(networkType)) {
int status = CoverageInfo.getCoverageStatus(networkType, false);
switch (status) {
// case CoverageInfo.COVERAGE_DIRECT: //TODO if we switch back to < 4.5 we must use CARRIER
// return COVERAGE_CARRIER;//not support less ver of 4.5
case CoverageInfo.COVERAGE_MDS:
return COVERAGE_MDS;
case CoverageInfo.COVERAGE_NONE:
return COVERAGE_NONE;
default:
break;
}
}
return NOT_SUPPORTED_WAF;
}
public static boolean isDataConnectionAvailable() {
boolean ret = (isWifiAvailable()&&isWifiActive())||!isWapGprsDataBearerOffline();
return ret;
}
/**
* Determines what connection type to use and returns the necessary string to use it.
* #return A string with the connection info
*/
public static String getSubURL()
{
// This code is based on the connection code developed by Mike Nelson of AccelGolf.
// http://blog.accelgolf.com/2009/05/22/blackberry-cross-carrier-and-cross-network-http-connection
String connectionString = null;
// Simulator behavior is controlled by the USE_MDS_IN_SIMULATOR variable.
if(DeviceInfo.isSimulator())
{
logMessage("Device is a simulator and USE_MDS_IN_SIMULATOR is false");
connectionString = ";deviceside=true";
}
// Wifi is the preferred transmission method
else if(WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
{
logMessage("Device is connected via Wifi.");
connectionString = ";interface=wifi";
}else{
String uid = null;
ServiceBook sb = ServiceBook.getSB();
ServiceRecord[] records = sb.findRecordsByCid("WPTCP");
for (int i = 0; i < records.length; i++) {
if (records[i].isValid() && !records[i].isDisabled()) {
if (records[i].getUid() != null &&
records[i].getUid().length() != 0) {
if ((records[i].getCid().toLowerCase().indexOf("wptcp") != -1) &&
(records[i].getUid().toLowerCase().indexOf("wifi") == -1) &&
(records[i].getUid().toLowerCase().indexOf("mms") == -1) ) {
uid = records[i].getUid();
break;
}
}
}
}
if (uid != null) {
// WAP2 Connection
connectionString = ";ConnectionUID="+uid;
} else {
connectionString = ";deviceside=true";
}
}
return connectionString + ";ConnectionTimeout=60000";
}
/**
* Looks through the phone's service book for a carrier provided BIBS network
* #return The uid used to connect to that network.
*/
private static String getCarrierBIBSUid()
{
ServiceRecord[] records = ServiceBook.getSB().getRecords();
int currentRecord;
for(currentRecord = 0; currentRecord < records.length; currentRecord++)
{
if(records[currentRecord].getCid().toLowerCase().equals("ippp"))
{
if(records[currentRecord].getName().toLowerCase().indexOf("bibs") >= 0)
{
return records[currentRecord].getUid();
}
}
}
return null;
}
public static void logMessage(String str)
{
logM=str;
}
public synchronized static String getConnectionString() {
String connectionString = null;
// Simulator behaviour is controlled by the USE_MDS_IN_SIMULATOR variable.
if (DeviceInfo.isSimulator()) {
connectionString = ";deviceside=true";
}
// Wifi is the preferred transmission method
else if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED) {
connectionString = ";interface=wifi";
}
// Is the carrier network the only way to connect?
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT) {
String carrierUid = getCarrierBIBSUid();
if (carrierUid == null) {
// Has carrier coverage, but not BIBS. So use the carrier's TCP network
connectionString = ";deviceside=true";
} else {
// otherwise, use the Uid to construct a valid carrier BIBS request
connectionString = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
}
}
// Check for an MDS connection instead (BlackBerry Enterprise Server)
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS) {
connectionString = ";deviceside=false";
}
// If there is no connection available abort to avoid hassling the user
// unnecssarily.
else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE) {
connectionString = "none";
}
// In theory, all bases are covered by now so this shouldn't be reachable.But hey, just in case ...
else {
connectionString = ";deviceside=true";
}
return connectionString;
}
}
Thanks very much in advance....
I don't know if it's the problem but you are not closing your ByteArrayOutputStream (call baos.close() ) before calling .toByteArray()

sshexec ant task: environment variables

I'm using SSHExec ant task to connect to a remote host and I depend on the environment variables that are set on the remote host in order to be able to successfully execute some commands.
<sshexec host="somehost"
username="${username}"
password="${password}"
command="set"/>
Using the task the env. variables that are outputed are not the same as the ones I get when I log in using an SSH Client.
How can I make the env. variables of the remote host avaiable for the session?
Actually there is something you can do about the fact it doesn't start a shell. Use the following:
<sshexec command="/bin/bash -l yourScript.sh" .../>
Using /bin/bash -l will start an login shell then execute your script within that shell. It would be exactly as if you had a version of sshexec that properly starts up a login shell. It has to be a script. If you want to run a single executable command you can do this:
<sshexec command="/bin/bash -l -c 'echo $CATALINA_HOME'" .../>
I've found out that the current SSHExeec task implementation is using JSCh's ChannelExec (remote execution of commands) instead of a ChannelShell (remote shell) as connection channel.
That means that apparentely as per JSCh's current implementation a ChannelExec doesn't load env. variables.
I'm still not sure wether this is a limitation on the protocol or on the API.
The conclusion is that as for now there's no solution for the problem, unless you implement your own Ant task.
A working draft of how it would be:
package org.apache.tools.ant.taskdefs.optional.ssh;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.StringReader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.KeepAliveOutputStream;
import org.apache.tools.ant.util.TeeOutputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Executes a command on a remote machine via ssh.
* #since Ant 1.6 (created February 2, 2003)
*/
public class SSHExecShellSupport extends SSHBase {
private static final String COMMAND_SEPARATOR = System.getProperty("line.separator");
private static final int BUFFER_SIZE = 8192;
private static final int RETRY_INTERVAL = 500;
/** the command to execute via ssh */
private String command = null;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
/** for waiting for the command to finish */
private Thread thread = null;
private String outputProperty = null; // like <exec>
private File outputFile = null; // like <exec>
private boolean append = false; // like <exec>
private Resource commandResource = null;
private boolean isShellMode;
private long maxTimeWithoutAnyData = 1000*10;
private static final String TIMEOUT_MESSAGE =
"Timeout period exceeded, connection dropped.";
public long getMaxTimeWithoutAnyData() {
return maxTimeWithoutAnyData;
}
public void setMaxTimeWithoutAnyData(long maxTimeWithoutAnyData) {
this.maxTimeWithoutAnyData = maxTimeWithoutAnyData;
}
public boolean isShellMode() {
return isShellMode;
}
public void setShellMode(boolean isShellMode) {
this.isShellMode = isShellMode;
}
/**
* Constructor for SSHExecTask.
*/
public SSHExecShellSupport() {
super();
}
/**
* Sets the command to execute on the remote host.
*
* #param command The new command value
*/
public void setCommand(String command) {
this.command = command;
}
/**
* Sets a commandResource from a file
* #param f the value to use.
* #since Ant 1.7.1
*/
public void setCommandResource(String f) {
this.commandResource = new FileResource(new File(f));
}
/**
* The connection can be dropped after a specified number of
* milliseconds. This is sometimes useful when a connection may be
* flaky. Default is 0, which means "wait forever".
*
* #param timeout The new timeout value in seconds
*/
public void setTimeout(long timeout) {
maxwait = timeout;
}
/**
* If used, stores the output of the command to the given file.
*
* #param output The file to write to.
*/
public void setOutput(File output) {
outputFile = output;
}
/**
* Determines if the output is appended to the file given in
* <code>setOutput</code>. Default is false, that is, overwrite
* the file.
*
* #param append True to append to an existing file, false to overwrite.
*/
public void setAppend(boolean append) {
this.append = append;
}
/**
* If set, the output of the command will be stored in the given property.
*
* #param property The name of the property in which the command output
* will be stored.
*/
public void setOutputproperty(String property) {
outputProperty = property;
}
/**
* Execute the command on the remote host.
*
* #exception BuildException Most likely a network error or bad parameter.
*/
public void execute() throws BuildException {
if (getHost() == null) {
throw new BuildException("Host is required.");
}
if (getUserInfo().getName() == null) {
throw new BuildException("Username is required.");
}
if (getUserInfo().getKeyfile() == null
&& getUserInfo().getPassword() == null) {
throw new BuildException("Password or Keyfile is required.");
}
if (command == null && commandResource == null) {
throw new BuildException("Command or commandResource is required.");
}
if(isShellMode){
shellMode();
} else {
commandMode();
}
}
private void shellMode() {
final Object lock = new Object();
Session session = null;
try {
session = openSession();
final Channel channel=session.openChannel("shell");
final PipedOutputStream pipedOS = new PipedOutputStream();
PipedInputStream pipedIS = new PipedInputStream(pipedOS);
final Thread commandProducerThread = new Thread("CommandsProducerThread"){
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(commandResource.getInputStream()));
String singleCmd;
synchronized (lock) {
lock.wait(); // waits for the reception of the very first data (before commands are issued)
while ((singleCmd = br.readLine()) != null) {
singleCmd += COMMAND_SEPARATOR;
log("cmd : " + singleCmd, Project.MSG_INFO);
pipedOS.write(singleCmd.getBytes());
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
break;
}
}
log("Finished producing commands", Project.MSG_VERBOSE);
}
} catch (IOException e) {
log(e, Project.MSG_VERBOSE);
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
} finally {
FileUtils.close(br);
}
}
};
ByteArrayOutputStream out = new ByteArrayOutputStream();
final TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.setInputStream(pipedIS);
channel.connect();
// waits for it to finish receiving data response and then ask for another the producer to issue one more command
thread = new Thread("DataReceiverThread") {
public void run() {
long lastTimeConsumedData = System.currentTimeMillis(); // initializes the watch
try {
InputStream in = channel.getInputStream();
byte[] tmp = new byte[1024];
while (true) {
if(thread == null){ // works with maxTimeout (for the whole task to complete)
break;
}
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
lastTimeConsumedData = System.currentTimeMillis();
if (i < 0){
break;
}
tee.write(tmp, 0, i);
}
if (channel.isClosed()) {
log("exit-status: " + channel.getExitStatus(), Project.MSG_INFO);
log("channel.isEOF(): " + channel.isEOF(), Project.MSG_VERBOSE);
log("channel.isConnected(): " + channel.isConnected(), Project.MSG_VERBOSE);
throw new BuildException("Connection lost."); // NOTE: it also can happen that if one of the command are "exit" the channel will be closed!
}
synchronized(lock){
long elapsedTimeWithoutData = (System.currentTimeMillis() - lastTimeConsumedData);
if (elapsedTimeWithoutData > maxTimeWithoutAnyData) {
log(elapsedTimeWithoutData / 1000 + " secs elapsed without any data reception. Notifying command producer.", Project.MSG_VERBOSE);
lock.notify(); // command producer is waiting for this
try {
lock.wait(500); // wait til we have new commands.
Thread.yield();
log("Continuing consumer loop. commandProducerThread.isAlive()?" + commandProducerThread.isAlive(), Project.MSG_VERBOSE);
if(!commandProducerThread.isAlive()){
log("No more commands to be issued and it's been too long without data reception. Exiting consumer.", Project.MSG_VERBOSE);
break;
}
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
break;
}
lastTimeConsumedData = System.currentTimeMillis(); // resets watch
}
}
}
} catch (IOException e) {
throw new BuildException(e);
}
}
};
thread.start();
commandProducerThread.start();
thread.join(maxwait);
if (thread.isAlive()) {
// ran out of time
thread = null;
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
//success
if (outputFile != null) {
writeToFile(out.toString(), append, outputFile);
}
// this is the wrong test if the remote OS is OpenVMS,
// but there doesn't seem to be a way to detect it.
log("Exit status (not reliable): " + channel.getExitStatus(), Project.MSG_INFO);
// int ec = channel.getExitStatus(); FIXME
// if (ec != 0) {
// String msg = "Remote command failed with exit status " + ec;
// if (getFailonerror()) {
// throw new BuildException(msg);
// } else {
// log(msg, Project.MSG_ERR);
// }
// }
}
} catch (Exception e){
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private void commandMode() {
Session session = null;
try {
session = openSession();
/* called once */
if (command != null) {
log("cmd : " + command, Project.MSG_INFO);
ByteArrayOutputStream out = executeCommand(session, command);
if (outputProperty != null) {
//#bugzilla 43437
getProject().setNewProperty(outputProperty, command + " : " + out);
}
} else { // read command resource and execute for each command
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(commandResource.getInputStream()));
String cmd;
String output = "";
while ((cmd = br.readLine()) != null) {
log("cmd : " + cmd, Project.MSG_INFO);
ByteArrayOutputStream out = executeCommand(session, cmd);
output += cmd + " : " + out + "\n";
}
if (outputProperty != null) {
//#bugzilla 43437
getProject().setNewProperty(outputProperty, output);
}
FileUtils.close(br);
} catch (IOException e) {
throw new BuildException(e);
}
}
} catch (JSchException e) {
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private ByteArrayOutputStream executeCommand(Session session, String cmd)
throws BuildException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
try {
final ChannelExec channel;
session.setTimeout((int) maxwait);
/* execute the command */
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(cmd);
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.connect();
// wait for it to finish
thread =
new Thread() {
public void run() {
while (!channel.isClosed()) {
if (thread == null) {
return;
}
try {
sleep(RETRY_INTERVAL);
} catch (Exception e) {
// ignored
}
}
}
};
thread.start();
thread.join(maxwait);
if (thread.isAlive()) {
// ran out of time
thread = null;
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
//success
if (outputFile != null) {
writeToFile(out.toString(), append, outputFile);
}
// this is the wrong test if the remote OS is OpenVMS,
// but there doesn't seem to be a way to detect it.
int ec = channel.getExitStatus();
if (ec != 0) {
String msg = "Remote command failed with exit status " + ec;
if (getFailonerror()) {
throw new BuildException(msg);
} else {
log(msg, Project.MSG_ERR);
}
}
}
} catch (BuildException e) {
throw e;
} catch (JSchException e) {
if (e.getMessage().indexOf("session is down") >= 0) {
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE, e);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(),
Project.MSG_ERR);
}
}
} catch (Exception e) {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
}
}
return out;
}
/**
* Writes a string to a file. If destination file exists, it may be
* overwritten depending on the "append" value.
*
* #param from string to write
* #param to file to write to
* #param append if true, append to existing file, else overwrite
* #exception Exception most likely an IOException
*/
private void writeToFile(String from, boolean append, File to)
throws IOException {
FileWriter out = null;
try {
out = new FileWriter(to.getAbsolutePath(), append);
StringReader in = new StringReader(from);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead == -1) {
break;
}
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
}
Another simple workaround is to source the user's .bash_profile before running your commands:
<sshexec host="somehost"
username="${username}"
password="${password}"
command="source ~/.bash_profile && set"/>
Great post chubbsondubs. I needed to set the ORACLE SID then execute a PLSQL script that does not have the proper exit. Hence the echo exit piped.
<sshexec host="${db.ipaddr}"
verbose="true"
trust="true"
username="${scp.oracle.userid}"
password="${scp.oracle.password}"
command="echo exit | /bin/bash -l -c 'export ORACLE_SID=${db.name} ; sqlplus ${db.dbo.userid}/${db.dbo.password} #./INSTALL_REVPORT/CreateDatabase/gengrant.sql'"
/>

Resources