/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2012 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others.
*
*See COPYING for Details
*
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package plugins.collaboration.socket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import plugins.collaboration.socket.SocketBasics.UnableToGetLockException;
import freemind.controller.actions.generated.instance.CollaborationActionBase;
import freemind.controller.actions.generated.instance.CollaborationGoodbye;
import freemind.controller.actions.generated.instance.CollaborationHello;
import freemind.controller.actions.generated.instance.CollaborationReceiveLock;
import freemind.controller.actions.generated.instance.CollaborationRequireLock;
import freemind.controller.actions.generated.instance.CollaborationTransaction;
import freemind.controller.actions.generated.instance.CollaborationUnableToLock;
import freemind.controller.actions.generated.instance.CollaborationUserInformation;
import freemind.controller.actions.generated.instance.CollaborationWelcome;
import freemind.controller.actions.generated.instance.CollaborationWhoAreYou;
import freemind.controller.actions.generated.instance.CollaborationWrongCredentials;
import freemind.extensions.PermanentNodeHook;
import freemind.main.Tools;
import freemind.modes.MapAdapter;
import freemind.modes.NodeAdapter;
import freemind.modes.mindmapmode.MindMapController;
import freemind.modes.mindmapmode.MindMapMapModel;
import freemind.modes.mindmapmode.MindMapNodeModel;
/**
* @author foltin
* @date 06.09.2012
*/
public class ClientCommunication extends CommunicationBase {
private static final int MAX_LOCK_RETRIES = 5;
private static final long LOCK_RETRY_SLEEP_TIME = 1000;
private String mLockId;
private HashSet mLockIds = new HashSet();
private String mPassword;
private SocketConnectionHook mSocketConnectionHook = null;
private boolean mReceivedGoodbye = false;
private CollaborationUserInformation mUserInfo;
/**
* @param pName
* @param pClient
* @param pController
* @param pPassword
* @param pOut
* @param pIn
* @throws IOException
*/
public ClientCommunication(String pName, Socket pClient,
MindMapController pController, String pPassword) throws IOException {
super(pName, pClient, pController, new DataOutputStream(
pClient.getOutputStream()), new DataInputStream(
pClient.getInputStream()));
mPassword = pPassword;
setCurrentState(STATE_WAIT_FOR_WHO_ARE_YOU);
}
/*
* (non-Javadoc)
*
* @see
* plugins.collaboration.socket.CommunicationBase#processCommand(freemind
* .controller.actions.generated.instance.CollaborationActionBase)
*/
public void processCommand(CollaborationActionBase pCommand)
throws IOException {
if (pCommand instanceof CollaborationGoodbye) {
CollaborationGoodbye goodbye = (CollaborationGoodbye) pCommand;
logger.info("Goodbye received from " + goodbye.getUserId());
terminateSocket();
return;
}
boolean commandHandled = false;
if (pCommand instanceof CollaborationUserInformation) {
CollaborationUserInformation userInfo = (CollaborationUserInformation) pCommand;
mUserInfo = userInfo;
commandHandled = true;
}
if (pCommand instanceof CollaborationWhoAreYou) {
if (getCurrentState() != STATE_WAIT_FOR_WHO_ARE_YOU) {
printWrongState(pCommand);
}
// send hello:
CollaborationHello helloCommand = new CollaborationHello();
helloCommand.setUserId(Tools.getUserName());
helloCommand.setPassword(mPassword);
send(helloCommand);
setCurrentState(STATE_WAIT_FOR_WELCOME);
commandHandled = true;
}
if (pCommand instanceof CollaborationWelcome) {
if (getCurrentState() != STATE_WAIT_FOR_WELCOME) {
printWrongState(pCommand);
}
CollaborationWelcome collWelcome = (CollaborationWelcome) pCommand;
createNewMap(collWelcome.getMap());
setCurrentState(STATE_IDLE);
commandHandled = true;
}
if (pCommand instanceof CollaborationWrongCredentials) {
if (getCurrentState() != STATE_WAIT_FOR_WELCOME) {
printWrongState(pCommand);
}
// Over and out.
terminateSocket();
// Display error message!
getMindMapController().getController().errorMessage(
getMindMapController().getText("socket_wrong_password"));
commandHandled = true;
}
if (pCommand instanceof CollaborationTransaction) {
CollaborationTransaction trans = (CollaborationTransaction) pCommand;
// check if it is from me!
boolean removeResult;
synchronized (mLockIds) {
removeResult = mLockIds.remove(trans.getId());
}
if (!removeResult) {
// it is not from me, so handle it:
if (getCurrentState() != STATE_IDLE) {
printWrongState(pCommand);
}
if (mSocketConnectionHook != null) {
mSocketConnectionHook
.executeTransaction(getActionPair(trans));
}
}
commandHandled = true;
}
if (pCommand instanceof CollaborationReceiveLock) {
if (getCurrentState() != STATE_WAIT_FOR_LOCK) {
printWrongState(pCommand);
}
CollaborationReceiveLock lockReceived = (CollaborationReceiveLock) pCommand;
this.mLockId = lockReceived.getId();
synchronized (mLockIds) {
mLockIds.add(mLockId);
}
setCurrentState(STATE_LOCK_RECEIVED);
commandHandled = true;
}
if (pCommand instanceof CollaborationUnableToLock) {
// no lock possible.
setCurrentState(STATE_IDLE);
commandHandled = true;
}
if (!commandHandled) {
logger.warning("Received unknown message of type "
+ pCommand.getClass());
}
}
public void terminateSocket() {
mReceivedGoodbye = true;
if (mSocketConnectionHook != null) {
// first deregister, as otherwise, the toggle hook command is tried
// to
// be sent over the wire.
mSocketConnectionHook.deregisterFilter();
// Terminates socket by shutdownHook.
toggleHook();
} else {
// Terminate socket.
shutdown();
}
}
public void toggleHook() {
SocketBasics.togglePermanentHook(getMindMapController(),
SocketBasics.SLAVE_HOOK_LABEL);
}
/**
* Sends the lock requests, blocks until timeout or answer and returns the
* associated id. Exception otherwise.
*
* @throws InterruptedException
* @throws UnableToGetLockException
*/
public synchronized String sendLockRequest() throws InterruptedException,
UnableToGetLockException {
// TODO: Global lock needed?
mLockId = null;
int lockTries = 0;
int timeout = 0;
while(lockTries < MAX_LOCK_RETRIES) {
CollaborationRequireLock lockRequest = new CollaborationRequireLock();
setCurrentState(STATE_WAIT_FOR_LOCK);
if (!send(lockRequest)) {
setCurrentState(STATE_IDLE);
throw new SocketBasics.UnableToGetLockException();
}
final int sleepTime = ROUNDTRIP_TIMEOUT / ROUNDTRIP_ROUNDS;
timeout = ROUNDTRIP_ROUNDS;
while (getCurrentState() == STATE_WAIT_FOR_LOCK && timeout >= 0) {
sleep(sleepTime);
timeout--;
}
if(getCurrentState() == STATE_LOCK_RECEIVED) {
break;
}
// error case, repeat lock question.
lockTries ++;
logger.info("Didn't receive a lock, current try: " + lockTries);
sleep(LOCK_RETRY_SLEEP_TIME);
}
setCurrentState(STATE_IDLE);
if(lockTries == MAX_LOCK_RETRIES || timeout < 0) {
throw new SocketBasics.UnableToGetLockException();
}
return mLockId;
}
void createNewMap(String map) throws IOException {
{
// // deregister from old controller:
// deregisterFilter();
logger.info("Restoring the map...");
MindMapController newModeController = (MindMapController) getMindMapController()
.getMode().createModeController();
MapAdapter newModel = new MindMapMapModel(getMindMapController()
.getFrame(), newModeController);
HashMap IDToTarget = new HashMap();
StringReader reader = new StringReader(map);
MindMapNodeModel rootNode = (MindMapNodeModel) newModeController
.createNodeTreeFromXml(reader, IDToTarget);
reader.close();
newModel.setRoot(rootNode);
rootNode.setMap(newModel);
getMindMapController().newMap(newModel);
newModeController.invokeHooksRecursively((NodeAdapter) rootNode,
newModel);
setController(newModeController);
// add new hook
toggleHook();
// tell him about this thread.
Collection activatedHooks = getMindMapController().getRootNode()
.getActivatedHooks();
for (Iterator it = activatedHooks.iterator(); it.hasNext();) {
PermanentNodeHook hook = (PermanentNodeHook) it.next();
if (hook instanceof SocketConnectionHook) {
SocketConnectionHook connHook = null;
connHook = (SocketConnectionHook) hook;
// Tell the hook about me
connHook.setClientCommunication(this);
/* register as listener, as I am a slave. */
connHook.registerFilter();
this.mSocketConnectionHook = connHook;
break;
}
}
}
}
/**
* @param pNewModeController
*/
private void setController(MindMapController pNewModeController) {
mController = pNewModeController;
}
/**
* @return
*/
private MindMapController getMindMapController() {
return mController;
}
/*
* (non-Javadoc)
*
* @see plugins.collaboration.socket.SocketBasics#shutdown()
*/
public void shutdown() {
// we don't want to read anymore, as we send the goodbye anyway!
commitSuicide();
try {
if (!mReceivedGoodbye) {
// Send only, if own goodbye.
CollaborationGoodbye goodbye = new CollaborationGoodbye();
goodbye.setUserId(Tools.getUserName());
send(goodbye);
// in between, the socket has been closed.
}
} catch (Exception e) {
freemind.main.Resources.getInstance().logException(e);
}
try {
close();
} catch (IOException e) {
freemind.main.Resources.getInstance().logException(e);
}
}
/**
* @return
*/
public int getPort() {
return mSocket.getLocalPort();
}
public CollaborationUserInformation getUserInfo() {
return mUserInfo;
}
}