/**
* Copyright 2011 multibit.org
*
* Licensed under the MIT license (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.php
*
* 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 net.bither;
import net.bither.bitherj.BitherjSettings;
import net.bither.db.AddressDBHelper;
import net.bither.db.TxDBHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public final class ApplicationInstanceManager {
private static final Logger log = LoggerFactory.getLogger(ApplicationInstanceManager.class);
private static ApplicationDataDirectoryLocator.ApplicationInstanceListener subListener;
public static final String MESSAGE_START = "$$BitherMessageStart$$\n";
public static final String MESSAGE_END = "\n$$X9Q3J7MessageEnd$$\n";
private static Thread instanceListenerThread;
private static boolean shutdownSocket = false;
public static TxDBHelper txDBHelper;
public static AddressDBHelper addressDBHelper;
/**
* Utility class should not have a public constructor
*/
private ApplicationInstanceManager() {
}
/**
* Registers this instance of the application. Passing in the rawURI that
* was passed in on the command line
*
* @return true if first instance, false if not.
*/
public static boolean registerInstance(String rawURI) {
// returnValueOnError should be true if lenient (allows application to
// run on network error) or false if strict.
boolean returnValueOnError = true;
// try to open network socket
// if success, listen to socket for new instance message, return true
// if unable to open, connect to existing and send new instance message,
// return false
try {
final ServerSocket socket = new ServerSocket(BitherjSettings.BITHER_DESKTOP_NETWORK_SOCKET, 10, InetAddress.getByAddress(new byte[]{127, 0,
0, 1}));
instanceListenerThread = new Thread(new Runnable() {
@Override
public void run() {
boolean socketClosed = false;
Socket client = null;
while (!socketClosed && !shutdownSocket) {
if (socket.isClosed()) {
socketClosed = true;
} else {
try {
client = socket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String messageStart = in.readLine();
if (MESSAGE_START.trim().equals(messageStart.trim())) {
log.debug("Message prefix matched - new application instance found");
StringBuffer messageBody = new StringBuffer();
boolean stillReading = true;
boolean firstLine = true;
while (stillReading) {
String currentLine = in.readLine();
if (currentLine == null) {
stillReading = false;
} else {
if (MESSAGE_END.trim().equals(currentLine.trim())) {
stillReading = false;
} else {
if (!firstLine) {
messageBody.append("\n");
}
firstLine = false;
// add message text
messageBody.append(currentLine);
}
}
}
log.debug("rawURI extracted from message as '" + messageBody.toString() + "'");
fireNewInstance(messageBody.toString());
}
in.close();
client.close();
} catch (IOException e) {
socketClosed = true;
}
}
}
// exited while due to shutdown request - shutdown socket
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
log.debug("Socket is shutdown.");
}
});
instanceListenerThread.start();
// listen
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
return returnValueOnError;
} catch (IOException e) {
log.debug("Port is already taken. Notifying first instance.");
try {
Socket clientSocket = new Socket(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), BitherjSettings.BITHER_DESKTOP_NETWORK_SOCKET);
OutputStream out = clientSocket.getOutputStream();
out.write(MESSAGE_START.getBytes());
if (rawURI != null) {
out.write(rawURI.getBytes());
}
out.write(MESSAGE_END.getBytes());
out.close();
clientSocket.close();
log.debug("Successfully notified first instance.");
return false;
} catch (UnknownHostException e1) {
log.error(e.getMessage(), e);
return returnValueOnError;
} catch (IOException e1) {
log.error("Error connecting to local port for single instance notification");
log.error(e1.getMessage(), e1);
return returnValueOnError;
}
}
return true;
}
public static void setApplicationInstanceListener(ApplicationDataDirectoryLocator.ApplicationInstanceListener listener) {
subListener = listener;
}
private static void fireNewInstance(String rawURI) {
if (subListener != null) {
subListener.newInstanceCreated(rawURI);
}
}
public static void shutdownSocket() {
log.debug("Making request to shut down socket ...");
shutdownSocket = true;
}
}