![JAR search and dependency download from the Maven repository](/logo.png)
org.jooq.debug.impl.MessagingInterface Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2009-2013, Lukas Eder, [email protected]
* Christopher Deckers, [email protected]
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.debug.impl;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jooq.debug.impl.Message.NoResult;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
class MessagingInterface {
private static final boolean IS_DEBUGGING_MESSAGES = Boolean.parseBoolean(System.getProperty("communication.interface.debug.printmessages"));
private static class CommandResultMessage extends Message {
private int originalID;
private S result;
private Throwable exception;
CommandResultMessage(int originalID, S result, Throwable exception) {
this.originalID = originalID;
this.result = result;
this.exception = exception;
}
public S getResult() {
return result;
}
public Throwable getException() {
return exception;
}
@Override
public String toString() {
return super.toString() + "(" + originalID + ")";
}
}
private Object RECEIVER_LOCK = new Object();
private ObjectOutputStream oos;
private ObjectInputStream ois;
private boolean isAlive = true;
public boolean isAlive() {
return isAlive;
}
public void destroy() {
isAlive = false;
try {
ois.close();
} catch(Exception e) {
}
}
private class MessageProcessingThread extends Thread {
private long originatorThreadID;
private List> messageList = new LinkedList>();
public MessageProcessingThread(long originatorThreadID) {
setName("Communication Interface Message Dispatcher-" + getId() + " [" + originatorThreadID + "]");
this.originatorThreadID = originatorThreadID;
}
public void addMessage(Message> message) {
synchronized (messageList) {
messageList.add(message);
}
}
public long getOriginatorThreadID() {
return originatorThreadID;
}
private boolean isWaitingOnSyncCall;
public boolean isWaitingOnSyncCall() {
return isWaitingOnSyncCall;
}
public void setWaitingOnSyncCall(boolean isWaitingOnSyncCall) {
this.isWaitingOnSyncCall = isWaitingOnSyncCall;
}
@Override
public void run() {
while(true) {
Message> message;
synchronized(messageList) {
if(messageList.isEmpty()) {
message = null;
} else {
message = messageList.remove(0);
}
}
// When there are no more messages to process, we try to de-register the current thread.
// We need to make sure that no message is posted while we do this.
if(message == null) {
synchronized (originatorThreadIDToThreadMap) {
synchronized(messageList) {
if(messageList.isEmpty()) {
originatorThreadIDToThreadMap.remove(originatorThreadID);
} else {
message = messageList.remove(0);
}
}
}
}
if(message == null) {
break;
}
runMessage(message);
}
}
}
private Map originatorThreadIDToThreadMap = new HashMap();
private Communication comm;
private boolean isClient;
MessagingInterface(final Communication communication, final Socket socket, boolean isClient) {
this.comm = communication;
this.isClient = isClient;
try {
oos = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()) {
@Override
public synchronized void write(int b) throws IOException {
super.write(b);
oosByteCount++;
}
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
oosByteCount += len;
}
});
oos.flush();
ois = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
} catch(IOException e) {
throw new RuntimeException(e);
}
Thread receiverThread = new Thread("MessagingInterface Receiver (" + (isClient? "client": "server") + ")") {
@Override
public void run() {
while(isAlive) {
Message> message = null;
try {
message = readMessage();
} catch(Exception e) {
if(isAlive) {
isAlive = false;
// e.printStackTrace();
try {
communication.notifyKilled();
} catch(Exception ex) {
ex.printStackTrace();
}
}
// Unlock all locked sync calls
synchronized(RECEIVER_LOCK) {
receivedMessageList.clear();
RECEIVER_LOCK.notify();
}
synchronized (idToThreadInfo) {
for(ThreadInfo> threadInfo: idToThreadInfo.values()) {
synchronized (threadInfo) {
threadInfo.notify();
}
}
}
}
if(message != null) {
long threadID = message.getThreadID();
// boolean isOriginatingSide = message instanceof CM_asyncExecResponse;
boolean isProcessorToOriginator = message.isProcessorToOriginator();
if(!isProcessorToOriginator) {
MessageProcessingThread messageProcessingThread;
boolean isNew = false;
synchronized (originatorThreadIDToThreadMap) {
messageProcessingThread = originatorThreadIDToThreadMap.get(threadID);
if(messageProcessingThread == null) {
messageProcessingThread = new MessageProcessingThread(threadID);
originatorThreadIDToThreadMap.put(threadID, messageProcessingThread);
isNew = true;
messageProcessingThread.addMessage(message);
} else {
if(messageProcessingThread.isWaitingOnSyncCall()) {
threadID = messageProcessingThread.getId();
isProcessorToOriginator = true;
} else {
messageProcessingThread.addMessage(message);
}
}
}
if(isNew) {
messageProcessingThread.start();
}
}
if(isProcessorToOriginator) {
ThreadInfo threadInfo;
synchronized (idToThreadInfo) {
threadInfo = idToThreadInfo.get(threadID);
}
if(threadInfo != null) {
if(message instanceof CommandResultMessage) {
throw new IllegalStateException("I need to indicate if command result message.");
}
synchronized (threadInfo) {
threadInfo.setMessage(message);
threadInfo.notify();
}
} else {
System.err.println("What to do?");
}
}
}
}
try {
oos.close();
} catch(Exception e) {
}
try {
ois.close();
} catch(Exception e) {
}
try {
socket.close();
} catch(Exception e) {
}
}
};
receiverThread.setDaemon(true);
receiverThread.start();
}
private CommandResultMessage runMessage(Message message) {
if(IS_DEBUGGING_MESSAGES) {
System.err.println("[" + (isClient? "client": "server") + "] >RUN: " + message.getID() + ", " + message);
}
CommandResultMessage commandResultMessage;
if(message instanceof CommandMessage) {
CommandMessage commandMessage = (CommandMessage)message;
S result = null;
Throwable throwable = null;
if (message.isValid()) {
try {
result = commandMessage.run(new MessageContext(comm));
}
catch (Throwable t) {
throwable = t;
}
}
if (commandMessage.isSyncExec()) {
commandResultMessage = new CommandResultMessage(commandMessage.getID(), result, throwable);
asyncSend(commandResultMessage);
}
else {
if (throwable != null) {
throwable.printStackTrace();
}
commandResultMessage = new CommandResultMessage(message.getID(), result, throwable);
}
} else {
commandResultMessage = new CommandResultMessage(message.getID(), null, null);
if (message.isSyncExec()) {
asyncSend(commandResultMessage);
}
}
if(IS_DEBUGGING_MESSAGES) {
System.err.println("[" + (isClient? "client": "server") + "] > receivedMessageList = new LinkedList>();
private static class CM_asyncExecResponse extends CommandMessage {
private final long threadID;
private final CommandResultMessage commandResultMessage;
public CM_asyncExecResponse(long threadID, CommandResultMessage commandResultMessage) {
this.threadID = threadID;
this.commandResultMessage = commandResultMessage;
}
@SuppressWarnings("unchecked")
@Override
public NoResult run(MessageContext context) {
MessagingInterface messagingInterface = context.getMessagingInterface();
ThreadInfo threadInfo;
synchronized (messagingInterface.idToThreadInfo) {
threadInfo = (ThreadInfo) messagingInterface.idToThreadInfo.get(threadID);
}
if(threadInfo == null) {
System.err.println("A sync call is missing.");
return null;
}
synchronized(threadInfo) {
threadInfo.setMessage(commandResultMessage);
threadInfo.notify();
}
return null;
}
}
private static class CM_asyncExec extends CommandMessage {
private final long threadID;
private final Message message;
CM_asyncExec(long threadID, Message message) {
this.threadID = threadID;
this.message = message;
}
@Override
public NoResult run(MessageContext context) {
message.setSyncExec(false);
// message.setCommunicationInterface(communicationInterface);
MessagingInterface messagingInterface = context.getMessagingInterface();
CommandResultMessage commandResultMessage = messagingInterface.runMessage(message);
CM_asyncExecResponse asyncExecResponse = new CM_asyncExecResponse(threadID, commandResultMessage);
messagingInterface.asyncSend(asyncExecResponse);
return null;
}
}
private static class ThreadInfo {
private boolean isValuePresent;
private Message message;
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
isValuePresent = true;
this.message = message;
}
public void clearMessage() {
isValuePresent = false;
message = null;
}
public boolean isValuePresent() {
return isValuePresent;
}
}
private Map> idToThreadInfo = new HashMap>();
private void printFailedInvocation(Message> message) {
System.err.println("[" + (isClient? "client": "server") + "] Failed messaging: " + message);
}
public S syncSend(Message message) {
Thread thread = Thread.currentThread();
long threadID = thread.getId();
ThreadInfo threadInfo = new ThreadInfo();
ThreadInfo> previousThreadInfo;
synchronized (idToThreadInfo) {
previousThreadInfo = idToThreadInfo.put(threadID, threadInfo);
}
if(thread instanceof MessageProcessingThread) {
synchronized (originatorThreadIDToThreadMap) {
((MessageProcessingThread)thread).setWaitingOnSyncCall(true);
}
}
CM_asyncExec asyncExec = new CM_asyncExec(threadID, message);
asyncSend(asyncExec);
CommandResultMessage commandResultMessage = null;
synchronized(threadInfo) {
while(commandResultMessage == null) {
while(!threadInfo.isValuePresent()) {
try {
threadInfo.wait();
} catch(Exception e) {
}
if(!isAlive()) {
idToThreadInfo.remove(threadID);
printFailedInvocation(message);
return null;
}
}
Message value = threadInfo.getMessage();
threadInfo.clearMessage();
if(value instanceof CommandResultMessage) {
commandResultMessage = (CommandResultMessage)value;
} else {
runMessage(value);
}
}
}
synchronized (idToThreadInfo) {
if(previousThreadInfo != null) {
idToThreadInfo.put(threadID, previousThreadInfo);
} else {
idToThreadInfo.remove(threadID);
}
}
if(previousThreadInfo == null &&thread instanceof MessageProcessingThread) {
synchronized (originatorThreadIDToThreadMap) {
((MessageProcessingThread)thread).setWaitingOnSyncCall(false);
}
}
return processCommandResult(commandResultMessage);
}
private S processCommandResult(CommandResultMessage message) {
if(IS_DEBUGGING_MESSAGES) {
System.err.println("[" + (isClient? "client": "server") + "] message) {
message.setSyncExec(false);
Thread thread = Thread.currentThread();
// If the message was sent by the other side, all returning messages need to know which originating thread they are bound to.
if(thread instanceof MessageProcessingThread) {
message.setThreadID(((MessageProcessingThread) thread).getOriginatorThreadID());
message.setProcessorToOriginator(true);
} else {
message.setThreadID(thread.getId());
message.setProcessorToOriginator(false);
}
try {
writeMessage(message);
} catch(Exception e) {
throw new IllegalStateException(e);
}
}
private static final int OOS_RESET_THRESHOLD;
static {
String maxByteCountProperty = System.getProperty("communication.interface.streamresetthreshold");
if(maxByteCountProperty != null) {
OOS_RESET_THRESHOLD = Integer.parseInt(maxByteCountProperty);
} else {
OOS_RESET_THRESHOLD = 500000;
}
}
private int oosByteCount;
private void writeMessage(Message> message) throws IOException {
if(!isAlive()) {
printFailedInvocation(message);
return;
}
if(IS_DEBUGGING_MESSAGES) {
System.err.println("[" + (isClient? "client": "server") + "] " + (message.isSyncExec()? "SENDS": "SENDA") + ": " + message.getID() + ", " + message);
}
synchronized(oos) {
oos.writeUnshared(message);
oos.flush();
// Messages are cached, so we need to reset() from time to time to clean the cache, or else we get an OutOfMemoryError.
if(oosByteCount > OOS_RESET_THRESHOLD) {
oos.reset();
oosByteCount = 0;
}
}
}
private Message> readMessage() throws IOException, ClassNotFoundException {
Object o = ois.readUnshared();
if(o instanceof Message) {
Message> message = (Message>)o;
if(IS_DEBUGGING_MESSAGES) {
System.err.println("[" + (isClient? "client": "server") + "] RECV: " + message.getID() + ", " + message);
}
// message.setCommunicationInterface(communicationInterface);
return message;
}
System.err.println("[" + (isClient? "client": "server") + "] Unknown message: " + o);
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy