/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/**
*
*/
package org.teiid.transport;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutionException;
import org.teiid.adminapi.AdminProcessingException;
import org.teiid.client.util.ExceptionHolder;
import org.teiid.client.util.ResultsFuture;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.crypto.CryptoException;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.net.socket.Message;
import org.teiid.net.socket.ServiceInvocationStruct;
import org.teiid.query.QueryPlugin;
import org.teiid.runtime.RuntimePlugin;
import org.teiid.transport.ClientServiceRegistryImpl.ClientService;
public class ServerWorkItem implements Runnable {
private final ClientInstance socketClientInstance;
private final Serializable messageKey;
private final Message message;
private final ClientServiceRegistryImpl csr;
public ServerWorkItem(ClientInstance socketClientInstance, Serializable messageKey, Message message, ClientServiceRegistryImpl server) {
this.socketClientInstance = socketClientInstance;
this.messageKey = messageKey;
this.message = message;
this.csr = server;
}
/**
* main entry point for remote method calls.
*/
public void run() {
Message result = null;
String loggingContext = null;
final boolean encrypt = !(message.getContents() instanceof ServiceInvocationStruct);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
try {
Thread.currentThread().setContextClassLoader(this.csr.getCallerClassloader());
} catch(Throwable t) {
// ignore
}
message.setContents(this.socketClientInstance.getCryptor().unsealObject(message.getContents()));
if (!(message.getContents() instanceof ServiceInvocationStruct)) {
throw new AssertionError("unknown message contents"); //$NON-NLS-1$
}
final ServiceInvocationStruct serviceStruct = (ServiceInvocationStruct)message.getContents();
final ClientService clientService = this.csr.getClientService(serviceStruct.targetClass.getName());
loggingContext = clientService.getLoggingContext();
Method m = clientService.getReflectionHelper().findBestMethodOnTarget(serviceStruct.methodName, serviceStruct.args);
Object methodResult;
try {
methodResult = m.invoke(clientService.getInstance(), serviceStruct.args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
if (ResultsFuture.class.isAssignableFrom(m.getReturnType()) && methodResult != null) {
ResultsFuture<Object> future = (ResultsFuture<Object>) methodResult;
future.addCompletionListener(new ResultsFuture.CompletionListener<Object>() {
public void onCompletion(
ResultsFuture<Object> completedFuture) {
Message asynchResult = new Message();
try {
asynchResult.setContents(completedFuture.get());
} catch (InterruptedException e) {
asynchResult.setContents(processException(e, clientService.getLoggingContext()));
} catch (ExecutionException e) {
asynchResult.setContents(processException(e.getCause(), clientService.getLoggingContext()));
}
sendResult(asynchResult, encrypt);
}
});
} else { // synch call
Message resultHolder = new Message();
resultHolder.setContents(methodResult);
result = resultHolder;
}
} catch (Throwable t) {
Message holder = new Message();
holder.setContents(processException(t, loggingContext));
result = holder;
} finally {
Thread.currentThread().setContextClassLoader(classLoader);
}
if (result != null) {
sendResult(result, encrypt);
}
}
void sendResult(Message result, boolean encrypt) {
if (encrypt) {
try {
result.setContents(socketClientInstance.getCryptor().sealObject(result.getContents()));
} catch (CryptoException e) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40071, e);
}
}
socketClientInstance.send(result, messageKey);
}
private Serializable processException(Throwable e, String context) {
if (context == null) {
context = LogConstants.CTX_TRANSPORT;
}
// Case 5558: Differentiate between system level errors and
// processing errors. Only log system level errors as errors,
// log the processing errors as warnings only
if (e instanceof TeiidProcessingException) {
logProcessingException(e, context);
} else if (e instanceof AdminProcessingException) {
logProcessingException(e, context);
} else {
LogManager.logError(context, e, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40017, this.socketClientInstance.getWorkContext().getSessionId()));
}
return new ExceptionHolder(e);
}
private void logProcessingException(Throwable e, String context) {
Throwable cause = e;
while (cause.getCause() != null && cause != cause.getCause()) {
cause = cause.getCause();
}
StackTraceElement elem = cause.getStackTrace()[0];
String msg = RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40011, e.getMessage(), this.socketClientInstance.getWorkContext().getSessionId(), e.getClass().getName(), elem);
if (LogManager.isMessageToBeRecorded(context, MessageLevel.DETAIL)) {
LogManager.logWarning(context, e, msg);
} else {
LogManager.logWarning(context, msg + QueryPlugin.Util.getString("stack_info")); ////$NON-NLS-1$
}
}
}