/******************************************************************************* * Copyright (c) 2004, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ /* */ package org.eclipse.jem.internal.proxy.remote; import java.io.*; import java.util.*; import java.util.logging.Level; import org.eclipse.jem.internal.proxy.common.CommandException; import org.eclipse.jem.internal.proxy.common.remote.*; import org.eclipse.jem.internal.proxy.common.remote.Commands.ValueObject; import org.eclipse.jem.internal.proxy.core.*; import org.eclipse.jem.internal.proxy.initParser.tree.*; /** * The Remote proxy version of Expression. * * @since 1.0.0 */ public class REMExpression extends Expression { private IREMExpressionConnection connection; private boolean closed; // Are we closed. protected Commands.ValueObject workerValue; // A worker object so that we don't need to keep creating one and throwing it away. protected Map beanTypeCache; // Use to cache pending BeanTypes. Used in conjunction with REMStandardBeanTypeFactory. protected Map methodsCache; // Use to cache pending expression method proxies. Used in conjunction with REMProxyConsants. protected Map fieldsCache; // Use to cache pending expression field proxies. Used in conjunction with REMProxyConsants. /* * This is very special list. It tries to eliminate unneeded traffic. For example a mark immediately followed by an endmark does * not need to be sent. Many expressions can look like: mark, endmark, endtransaction. This is a do nothing and we don't want * to create a connection to just send this. So this list is used to queue up these and remove them too when found as not needed. * * However, this is very tricky because all pushToProxy transactions that actually do something MUST call the processPending() method * first to make sure any pending transactions are submitted. Because once a real type transaction, such as assignment occurs, any * pending transaction is a valid transaction, and no longer a do-nothing transaction. * * Each transaction type uses a subclass of PendingTransaction to be an entry on the list. * * The pendings currently supported are: * mark/endmark * try/catch/endtry * block/endblock * * See each individual transaction type to see how it is handled. */ protected List pendingTransactions; /** * PendingTransaction entry. * * @since 1.1.0 */ protected abstract static class PendingTransaction { /** * The transaction is now being pushed. The implementation should * actually do the push. * * @param remExpression The REMExpression for this transaction. * * @since 1.1.0 */ public abstract void pushTransaction(REMExpression remExpression); } /** * @param registry * * @since 1.0.0 */ public REMExpression(REMProxyFactoryRegistry registry) { super(registry); } /** * Return the expression id for this REMExpression. This id is used on the remote vm to * identify who the request is for. * @return * * @since 1.1.0 */ protected int getREMExpressionID() { return this.hashCode(); } /** * Get the pending transactions list. * @return * * @since 1.1.0 */ protected List getPendingTransactions() { if (pendingTransactions == null) pendingTransactions = new ArrayList(); return pendingTransactions; } // Use this flag when debugging to test if errors are due to improper pending processing. // If true they will be treated as if not pending and will be executed immediately. private static final boolean EXECUTE_PENDING_IMMEDIATELY = false; protected void addPendingTransaction(PendingTransaction pending) { if (!EXECUTE_PENDING_IMMEDIATELY) getPendingTransactions().add(pending); else pending.pushTransaction(this); } private boolean sentData; // Flag to indicate if we have sent anything yet to the remote vm. This is used for the pending optimizations. /** * Have we sent any data in this transaction yet. * @return * * @since 1.1.0 */ protected boolean haveSentData() { return sentData; } /** * @return Returns the connection. * * @since 1.1.0 */ protected IREMExpressionConnection getConnection() { if (connection == null) { if (!sentData) getREMBeanProxyFactory().startTransaction(); // This is the first time we send data, so start transaction. sentData = true; // If we are getting a transaction, that means we are sending data. connection = (IREMExpressionConnection) getREMRegistry().getFreeConnection(); // This will actually not be stopped until closeproxy. There could be a slight problem if the expression is never closed. // But that shouldn't happen. This is to prevent any proxy that was released during the execution but was used by // the expression from being released on the remote vm until after the expression is finished. try { if (workerValue == null) workerValue = new Commands.ValueObject(); if (expressionProcesserController == null) { byte trace = !isTraceSet() ? ExpressionCommands.TRACE_DEFAULT : (isTrace() ? ExpressionCommands.TRACE_ON : ExpressionCommands.TRACE_OFF); connection.startExpressionProcessing(getREMExpressionID(), trace); // It is a new expression. } else { fillProxy(expressionProcesserController, workerValue); connection.resumeExpression(getREMExpressionID(), workerValue); expressionProcesserController = null; } } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { connection.close(); connection = null; } throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } return connection; } /** * General IOException occurred msg. */ protected static final String IO_EXCEPTION_MSG = ProxyRemoteMessages.REMExpression_IOExceptionSeeLog_INFO_; protected static final String COMMAND_EXCEPTION_MSG = ProxyRemoteMessages.REMExpression_CommandExceptionSeeLog_INFO_; /** * Throw an an illegal state exception if some general error, in particular an I/O or Command Exception * occurred so that callers know there is something wrong. * * @param msg * @throws IllegalStateException * * @since 1.0.0 */ protected void throwIllegalStateException(String msg) throws IllegalStateException { throw new IllegalStateException(msg); } /** * Return the registry as a REMProxyFactoryRegistry * @return * * @since 1.0.0 */ protected final REMProxyFactoryRegistry getREMRegistry() { return (REMProxyFactoryRegistry) registry; } /** * Return the bean proxy factory as a REMStandardBeanProxyFactory. * @return * * @since 1.0.0 */ protected final REMStandardBeanProxyFactory getREMBeanProxyFactory() { return (REMStandardBeanProxyFactory) beanProxyFactory; } /** * Process any pending transactions. * <p> * <b>Note: </b>It is required that all non-pending-participating transactions must * call this method first to make sure pending transactions are sent. If this is * not done, there will be errors in the expression. * * * @since 1.1.0 */ protected void processPendingTransactions() { if (pendingTransactions != null && !pendingTransactions.isEmpty()) { try { for (int i = 0; i < pendingTransactions.size(); i++) { ((PendingTransaction) pendingTransactions.get(i)).pushTransaction(this); } } finally { pendingTransactions.clear(); } } } /** * Get the pending entry from top. If top is 1, then get top entry (i.e. last one added), 2 is next one. * @param fromTop * @return entry requested, or <code>null</code> if no such entry. * * @since 1.1.0 */ protected PendingTransaction getPendingEntryFromTop(int fromTop) { if (pendingTransactions != null && pendingTransactions.size() >= fromTop) { return (PendingTransaction) pendingTransactions.get(pendingTransactions.size()-fromTop); } else return null; } /** * Pop up the top entry from the pending transactions queue. * @param fromTop how many entries to pop from the pending transaction list. * * * @since 1.1.0 */ protected void popPendingEntry(int fromTop) { if (pendingTransactions != null) if (pendingTransactions.size() > fromTop) { while(fromTop-- >0) pendingTransactions.remove(pendingTransactions.size()-1); } else pendingTransactions.clear(); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushToProxy(org.eclipse.jem.internal.proxy.core.IProxy) */ protected void pushToProxy(IProxy proxy) { if (proxy == null || proxy.isBeanProxy()) pushToProxy((IBeanProxy) proxy); else pushToExpressionProxy((ExpressionProxy) proxy); } private void pushToProxy(IBeanProxy proxy) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push proxy command is: // PushExpressionCommand(push to proxy) followed by: // ValueObject containing the rendered proxy. connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_TO_PROXY_EXPRESSION_VALUE); if (proxy == null) workerValue.set(); else ((IREMBeanProxy) proxy).renderBean(workerValue); connection.pushValueObject(workerValue); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#closeProxy() */ protected void closeProxy() { if (!closed) { try { if (connection != null && connection.isConnected()) { try { connection.stopExpressionProcessing(getREMExpressionID()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e, Level.INFO); // Not throwing an illegal state here because we don't care, other than logging and not // returning the connection to the registry that there was an error on close. } finally { getREMRegistry().returnConnection(connection); } } } finally { closed = true; if (sentData) getREMBeanProxyFactory().stopTransaction(); // Resume proxy releases. We've sent data at least once. } } methodsCache = null; fieldsCache = null; beanTypeCache = null; pendingTransactions = null; connection = null; } private static final Object VOIDTYPE = new Object(); // A void type was sent in expression proxy resolution. private static final Object NOTRESOLVED = new Object(); // A not resolved type was sent in expression proxy resolution. /* * Get the sender to use for pulling the expression proxy resolutions. */ private BeanProxyValueSender getExpressionProxiesSender() { return new BeanProxyValueSender(getREMBeanProxyFactory()) { /* * (non-Javadoc) * * @see org.eclipse.jem.internal.proxy.remote.BeanProxyValueSender#sendValue(org.eclipse.jem.internal.proxy.common.remote.Commands.ValueObject) */ public void sendValue(ValueObject value) { if (value.getType() == Commands.FLAG) { switch (value.anInt) { case ExpressionCommands.EXPRESSIONPROXY_NOTRESOLVED: array[index++] = NOTRESOLVED; break; case ExpressionCommands.EXPRESSIONPROXY_VOIDTYPE: array[index++] = VOIDTYPE; break; default: // Shouldn't happen. break; } } else super.sendValue(value); } }; } /* * Process the pulled expression proxy resolutions. */ private void processpulledExpressionProxies(List expressionProxies, BeanProxyValueSender sender) { // It is expected that each entry will coorespond to the next non-null expression proxy and will be the bean proxy or one of the special // types. int len = expressionProxies.size(); int j = 0; Object[] resolveds = sender.getArray(); for (int i = 0; i < len; i++) { ExpressionProxy ep = (ExpressionProxy) expressionProxies.get(i); if (ep != null) { Object resolved = resolveds[j++]; if (resolved == NOTRESOLVED) fireProxyNotResolved(ep); else if (resolved == VOIDTYPE) fireProxyVoid(ep); else fireProxyResolved(ep, (IBeanProxy) resolved); } } } /* * (non-Javadoc) * * @see org.eclipse.jem.internal.proxy.core.Expression#pullProxyValue(int, java.util.List) */ protected IBeanProxy pullProxyValue(int proxycount, List expressionProxies) throws ThrowableProxy, NoExpressionValueException { if (!haveSentData()) { markAllProxiesNotResolved(expressionProxies); return null; // We haven't pushed any commands, so there is nothing to do. Don't create a connection for this. } // If there are any pending transactions at this point in time, there is no need to send them. They would be do nothings anyway. boolean processedExpressionProxies = false; IREMExpressionConnection lclConnection = getConnection(); markInTransaction(lclConnection); try { Commands.ValueObject proxyids = null; BeanProxyValueSender sender = null; if (proxycount > 0) { proxyids = createExpressionProxiesValueObject(proxycount, expressionProxies); sender = getExpressionProxiesSender(); } lclConnection.pullValue(getREMExpressionID(), proxyids, sender); // If we got this far, then if there are proxies, we need to process these too. if (proxycount > 0) processpulledExpressionProxies(expressionProxies, sender); processedExpressionProxies =true; lclConnection.getFinalValue(workerValue); // Get the returned value. return getREMBeanProxyFactory().getBeanProxy(workerValue); } catch (CommandErrorException e) { try { if (e.getErrorCode() == ExpressionCommands.EXPRESSION_NOEXPRESSIONVALUE_EXCEPTION) { // Need to turn it into a Throwable. ThrowableProxy t = null; try { getREMBeanProxyFactory().getBeanProxy(e.getValue()); // This will cause a throw to occur, but we don't want it going out, we want to capture it. } catch (ThrowableProxy e1) { t = e1; } throw new REMNoExpressionValueException(t); } getREMBeanProxyFactory().processErrorReturn(e); } catch (CommandException e1) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { lclConnection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { lclConnection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } finally { markEndTransaction(lclConnection); if (!processedExpressionProxies) markAllProxiesNotResolved(expressionProxies); // We failed before we could process the expression proxies. So mark all as not resolved. } return null; } /** * This is called by commands that write some data and will be reading data back immediately * (i.e. pull value and invoke expression). If we are on a callback thread and have the * used the connection from the callback thread, we need to tell the callback thread that * it is in a transaction. This is needed because while reading data back there are * sometimes calls back to the vm to get beantype data for new classes. This would * normally be through a new connection so that it doesn't get stuck in the middle of the * data being sent back. But when running on a callback the same connection is used. So it * would stick data in the middle of the return stream of data. To prevent this we need * to tell the callback thread that it is in a transaction during this call so that any * such new connection requests will get a new connection. * <p> * This is not nestable (i.e. the first markEndTransaction will set it false, even if several nested * markInTransactions are called). * <p> * markEndTransaction must be called in ALL cases, such use try/finally. * @param remConnection the connection to see check against and mark in transaction for. * * * @since 1.1.0 */ protected void markInTransaction(IREMExpressionConnection remConnection) { Thread thread = Thread.currentThread(); if (thread instanceof REMCallbackThread) { // We are in a callback, and the callback connection is our connection, tell the callback that it is in transaction. REMCallbackThread callbackThread = (REMCallbackThread) thread; if (callbackThread.getConnection() == remConnection) { callbackThread.setIntransaction(true); } } } /** * Mark end of transaction. * @param remConn REMConnection to test and mark not in connection for. * * @see REMExpression#markInTransaction(IREMExpressionConnection) * @since 1.1.0 */ protected void markEndTransaction(IREMExpressionConnection remConn) { Thread thread = Thread.currentThread(); if (thread instanceof REMCallbackThread) { // We are in a callback, and the callback connection is our connection, tell the callback that it is in transaction. REMCallbackThread callbackThread = (REMCallbackThread) thread; if (callbackThread.getConnection() == remConn) { callbackThread.setIntransaction(false); } } } /** * @param expressionProxies * * @since 1.1.0 */ private Commands.ValueObject createExpressionProxiesValueObject(int actualCount, List expressionProxies) { class ExpressionProxyRetriever implements Commands.ValueRetrieve { Iterator expressionProxiesItr; Commands.ValueObject worker = new Commands.ValueObject(); public ExpressionProxyRetriever(List expressionProxies) { this.expressionProxiesItr = expressionProxies.iterator(); } public Commands.ValueObject nextValue() { worker.set(-1); while (expressionProxiesItr.hasNext()) { Object parm = expressionProxiesItr.next(); if (parm != null) { worker.set(((ExpressionProxy) parm).getProxyID()); break; } } return worker; } }; workerValue.setArrayIDS(new ExpressionProxyRetriever(expressionProxies), actualCount, Commands.INT); return workerValue; } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushCastToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType) */ protected void pushCastToProxy(IProxyBeanType type) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push cast to proxy command is: // PushExpressionCommand(push cast to proxy) followed by: connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CAST_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /** * Push the proxy bean type in the format depending on expression proxy or beantype proxy. * @param type * @throws IOException * * @since 1.1.0 */ protected void fillProxy(IProxy type, Commands.ValueObject value) throws IOException { // ValueObject containing the rendered bean type proxy if IBeanTypeProxy or int (for expression proxy id) if expression proxy. if (type.isBeanProxy()) { ((IREMBeanProxy) type).renderBean(value); } else { ExpressionProxy ep = (ExpressionProxy) type; value.set(ep.getProxyID()); } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushInstanceofToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType) */ protected void pushInstanceofToProxy(IProxyBeanType type) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push instanceof to proxy command is: // PushExpressionCommand(push instanceof to proxy) followed by: // ValueObject containing the rendered bean type proxy or the String representing the name of class. connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.INSTANCEOF_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushInfixToProxy(org.eclipse.jem.internal.proxy.initParser.tree.InfixOperator, int) */ protected void pushInfixToProxy(InfixOperator operator, InternalInfixOperandType operandType) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push infix to proxy command is: // PushExpressionCommand(push infix to proxy) followed by: // byte: operator // byte: operandType connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.INFIX_EXPRESSION_VALUE); connection.pushByte((byte) operator.getValue()); connection.pushByte((byte) operandType.getValue()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushPrefixToProxy(org.eclipse.jem.internal.proxy.initParser.tree.PrefixOperator) */ protected void pushPrefixToProxy(PrefixOperator operator) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push prefix to proxy command is: // PushExpressionCommand(push prefix to proxy) followed by: // byte: operator connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PREFIX_EXPRESSION_VALUE); connection.pushByte((byte) operator.getValue()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushArrayAccessToProxy(int) */ protected void pushArrayAccessToProxy(int indexCount) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push array access to proxy command is: // PushExpressionCommand(push array acces to proxy) followed by: // int: indexCount connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_ACCESS_EXPRESSION_VALUE); connection.pushInt(indexCount); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushArrayCreationToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType, int) */ protected void pushArrayCreationToProxy(IProxyBeanType type, int dimensionCount) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push array creation to proxy command is: // PushExpressionCommand(push array creation to proxy) followed by: // ValueObject containing the rendered bean type proxy or the expression proxy. // int: dimension count connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_CREATION_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); connection.pushInt(dimensionCount); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } protected void pushArrayInitializerToProxy(IProxyBeanType type, int stripCount, int expressionCount) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push array initializer to proxy command is: // PushExpressionCommand(push array initializer to proxy) followed by: // ValueObject containing the rendered bean type proxy or expression proxy. // int: strip count // int: expression count connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_INITIALIZER_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); connection.pushInt(stripCount); connection.pushInt(expressionCount); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushClassInstanceCreationToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType, int) */ protected void pushClassInstanceCreationToProxy(IProxyBeanType type, int argumentCount) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push class instance creation to proxy command is: // PushExpressionCommand(push class instance creation to proxy) followed by: // ValueObject containing the rendered bean type proxy or the expression proxy // int: argument count connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CLASS_INSTANCE_CREATION_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); connection.pushInt(argumentCount); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushTypeReceiverToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType) */ protected void pushTypeReceiverToProxy(IProxyBeanType type) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push type receiver to proxy command is: // PushExpressionCommand(push type receiver to proxy) followed by: // ValueObject containing the rendered bean type proxy or the expression proxy. connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.TYPERECEIVER_EXPRESSION_VALUE); fillProxy(type, workerValue); connection.pushValueObject(workerValue); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushFieldAccessToProxy(java.lang.String, boolean) */ protected void pushFieldAccessToProxy(Object field, boolean hasReceiver) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push field access to proxy command is: // PushExpressionCommand(push field access to proxy) followed by: // Commands.Value: fieldName or IProxyField // boolean: hasReceiver connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.FIELD_ACCESS_EXPRESSION_VALUE); if (field instanceof String) { workerValue.set((String) field); } else { fillProxy((IProxy) field, workerValue); } connection.pushValueObject(workerValue); connection.pushBoolean(hasReceiver); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushMethodInvocationToProxy(java.lang.String, boolean, int) */ protected void pushMethodInvocationToProxy(Object method, boolean hasReceiver, int argCount) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push method invocation to proxy command is: // PushExpressionCommand(push method invocation to proxy) followed by: // Commands.ValueObject: methodName or IMethodProxy // boolean: hasReceiver // int: argCount connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.METHOD_EXPRESSION_VALUE); if (method instanceof String) { workerValue.set((String) method); } else { fillProxy((IProxy) method, workerValue); } connection.pushValueObject(workerValue); connection.pushBoolean(hasReceiver); connection.pushInt(argCount); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushConditionalToProxy(int) */ protected void pushConditionalToProxy(InternalConditionalOperandType expressionType) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push conditional to proxy command is: // PushExpressionCommand(push conditional to proxy) followed by: // byte: expression type connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CONDITIONAL_EXPRESSION_VALUE); connection.pushByte((byte) expressionType.getValue()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* * A special one that takes the ThrowableProxy for no expression value and * wrappers it prints its stack trace instead, but still makes it a subclass * of NoExpressionValueException. * * @since 1.1.0 */ private static class REMNoExpressionValueException extends NoExpressionValueException { /** * Comment for <code>serialVersionUID</code> * * @since 1.1.0 */ private static final long serialVersionUID = 1692406777391812694L; public REMNoExpressionValueException(ThrowableProxy e) { super(e); } /* (non-Javadoc) * @see java.lang.Throwable#getLocalizedMessage() */ public String getLocalizedMessage() { return ((ThrowableProxy) getCause()).getProxyLocalizedMessage(); } /* (non-Javadoc) * @see java.lang.Throwable#getMessage() */ public String getMessage() { return ((ThrowableProxy) getCause()).getProxyMessage(); } /* (non-Javadoc) * @see java.lang.Throwable#printStackTrace() */ public void printStackTrace() { getCause().printStackTrace(); } /* (non-Javadoc) * @see java.lang.Throwable#printStackTrace(java.io.PrintStream) */ public void printStackTrace(PrintStream s) { getCause().printStackTrace(s); } /* (non-Javadoc) * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter) */ public void printStackTrace(PrintWriter s) { getCause().printStackTrace(s); } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushInvoke(int, java.util.List) */ protected void pushInvoke(int proxycount, List expressionProxies) throws ThrowableProxy, NoExpressionValueException { if (!haveSentData()) { markAllProxiesNotResolved(expressionProxies); return; // We haven't pushed any commands, so there is nothing to do. Don't create a connection for this. } // If at this point there are pending transactions, there is no need to send them because they would all be do-nothings. boolean processedExpressionProxies = false; IREMExpressionConnection lclConnection = getConnection(); markInTransaction(lclConnection); try { Commands.ValueObject proxyids = null; BeanProxyValueSender sender = null; if (proxycount > 0) { proxyids = createExpressionProxiesValueObject(proxycount, expressionProxies); sender = getExpressionProxiesSender(); } lclConnection.sync(getREMExpressionID(), proxyids, sender); // If we got this far, then if there are proxies, we need to process these too. if (proxycount > 0) processpulledExpressionProxies(expressionProxies, sender); processedExpressionProxies = true; lclConnection.getFinalValue(workerValue); // We don't care what it is, we just need to see if there is an error. } catch (CommandErrorException e) { try { if (e.getErrorCode() == ExpressionCommands.EXPRESSION_NOEXPRESSIONVALUE_EXCEPTION) { // Need to turn it into a Throwable. ThrowableProxy t = null; try { getREMBeanProxyFactory().getBeanProxy(e.getValue()); // This will cause a throw to occur, but we don't want it going out, we want to capture it. } catch (ThrowableProxy e1) { t = e1; } throw new REMNoExpressionValueException(t); } getREMBeanProxyFactory().processErrorReturn(e); } catch (CommandException e1) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { lclConnection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { lclConnection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } finally { markEndTransaction(lclConnection); if (!processedExpressionProxies) markAllProxiesNotResolved(expressionProxies); // We failed before we could process the expression proxies. So mark all as not resolved. } } private static class REMBeanTypeExpressionProxy extends ExpressionProxy implements IBeanTypeExpressionProxy { private String typeName; /** * @param proxyid * * @since 1.1.0 */ private REMBeanTypeExpressionProxy(int proxyid, Expression expression) { super(proxyid, BEANTYPE_EXPRESSION_PROXY, expression); } public void setTypeName(String typeName) { this.typeName = typeName; } public String getTypeName() { return typeName; } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.ExpressionProxy#toString() */ public String toString() { return super.toString()+" - "+getTypeName(); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getMethodProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType[]) */ public IProxyMethod getMethodProxy(IExpression expression, String methodName, IProxyBeanType[] parameterTypes) { REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry(); return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getMethodProxy(expression, this, methodName, parameterTypes); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getMethodProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String, java.lang.String[]) */ public IProxyMethod getMethodProxy(IExpression expression, String methodName, String[] parameterTypes) { REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry(); return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getMethodProxy(expression, this, methodName, parameterTypes); } public IProxyMethod getMethodProxy(IExpression expression, String methodName) { return getMethodProxy(expression, methodName, (IProxyBeanType[]) null); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getFieldProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String) */ public IProxyField getFieldProxy(IExpression expression, String fieldName) { REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry(); return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getFieldProxy(expression, this, fieldName); } } private static class REMMethodExpressionProxy extends ExpressionProxy implements IProxyMethod { /** * @param proxyid * @param proxyType * @param expression * * @since 1.1.0 */ private REMMethodExpressionProxy(int proxyid, Expression expression) { super(proxyid, METHOD_EXPRESSION_PROXY, expression); } } private static class REMFieldExpressionProxy extends ExpressionProxy implements IProxyField { /** * @param proxyid * @param proxyType * @param expression * * @since 1.1.0 */ private REMFieldExpressionProxy(int proxyid, Expression expression) { super(proxyid, FIELD_EXPRESSION_PROXY, expression); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#createExpressionProxy(int) */ protected ExpressionProxy createExpressionProxy(int proxyType, int proxyID) { switch (proxyType) { case NORMAL_EXPRESSION_PROXY: default: return new ExpressionProxy(proxyID, NORMAL_EXPRESSION_PROXY, this); case BEANTYPE_EXPRESSION_PROXY: return new REMBeanTypeExpressionProxy(proxyID, this); case METHOD_EXPRESSION_PROXY: return new REMMethodExpressionProxy(proxyID, this); case FIELD_EXPRESSION_PROXY: return new REMFieldExpressionProxy(proxyID, this); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushAssignmentToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy) */ protected void pushAssignmentToProxy(ExpressionProxy proxy) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push assignment to proxy command is: // PushExpressionCommand(push assignment to proxy) followed by: // int: proxy id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ASSIGNMENT_PROXY_EXPRESSION_VALUE); connection.pushInt(proxy.getProxyID()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushAssignmentToProxy() */ protected void pushAssignmentToProxy() { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of the push assignment command is: // PushAssignmentCommand. connection.pushExpressionCommand(getREMExpressionID(), (byte) InternalExpressionTypes.ASSIGNMENT_EXPRESSION_VALUE); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } private void pushToExpressionProxy(ExpressionProxy proxy) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to expression proxy command is: // PushExpressionCommand(push expression proxy to proxy) followed by: // int: proxy id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_TO_EXPRESSION_PROXY_EXPRESSION_VALUE); connection.pushInt(proxy.getProxyID()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } private static class BlockBegin extends PendingTransaction { public int blockNumber; public BlockBegin(int blockNumber) { this.blockNumber = blockNumber; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to block begin proxy command is: // PushExpressionCommand(push block begin proxy to proxy) followed by: // int: block id connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_BEGIN_EXPRESSION_VALUE); connection.pushInt(blockNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockBeginToProxy(int) */ protected void pushBlockBeginToProxy(int blockNumber) { addPendingTransaction(new BlockBegin(blockNumber)); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockEndToProxy(int) */ protected void pushBlockEndToProxy(int blockNumber) { // See if the top pending transactions is BreakBlock(blockNumber). If it is then the BreakBlock can be thrown away. PendingTransaction topEntry = getPendingEntryFromTop(1); if (topEntry instanceof BlockBreak && ((BlockBreak) topEntry).blockNumber == blockNumber) { popPendingEntry(1); topEntry = getPendingEntryFromTop(1); } // See if the top pending transaction is now BeginBlock(blockNumber). If it is, then this transaction and the block begin // can be thrown away because they are an empty block. if (topEntry instanceof BlockBegin && ((BlockBegin) topEntry).blockNumber == blockNumber) { popPendingEntry(1); return; } processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to block end proxy command is: // PushExpressionCommand(push block end proxy to proxy) followed by: // int: block id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_END_EXPRESSION_VALUE); connection.pushInt(blockNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } private static class BlockBreak extends PendingTransaction { public int blockNumber; public BlockBreak(int blockNumber) { this.blockNumber = blockNumber; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to block break proxy command is: // PushExpressionCommand(push block break proxy to proxy) followed by: // int: block id connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_BREAK_EXPRESSION_VALUE); connection.pushInt(blockNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockBreakToProxy(int) */ protected void pushBlockBreakToProxy(int blockNumber) { // Even if there is no pending block begin for this block, we will pend the break. // This is so that if the break occurred just before the block end, then it can be ignored. addPendingTransaction(new BlockBreak(blockNumber)); } private static class TryBegin extends PendingTransaction { public final int tryNumber; public TryBegin(int tryNumber) { this.tryNumber = tryNumber; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to try begin proxy command is: // PushExpressionCommand(push try begin to proxy) followed by: // int: try id connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_BEGIN_EXPRESSION_VALUE); connection.pushInt(tryNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryBeginToProxy(int) */ protected void pushTryBeginToProxy(int tryNumber) { addPendingTransaction(new TryBegin(tryNumber)); } private static class TryCatch extends PendingTransaction { public final int tryNumber; private final IProxyBeanType exceptionType; private final ExpressionProxy ep; public TryCatch(int tryNumber, IProxyBeanType exceptionType, ExpressionProxy ep) { this.tryNumber = tryNumber; this.exceptionType = exceptionType; this.ep = ep; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to try begin proxy command is: // PushExpressionCommand(push try begin to proxy) followed by: // int: try id // object: expression type (as beantype or as expression proxy) // int: proxy id or (-1 if null). connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_CATCH_EXPRESSION_VALUE); connection.pushInt(tryNumber); remExpression.fillProxy(exceptionType, remExpression.workerValue); connection.pushValueObject(remExpression.workerValue); if (ep != null) connection.pushInt(ep.getProxyID()); else connection.pushInt(-1); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); remExpression.throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } } /* * (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryCatchClauseToProxy(int, org.eclipse.jem.internal.proxy.core.IProxyBeanType, org.eclipse.jem.internal.proxy.core.ExpressionProxy) */ protected void pushTryCatchClauseToProxy(int tryNumber, IProxyBeanType exceptionType, ExpressionProxy ep) { addPendingTransaction(new TryCatch(tryNumber, exceptionType, ep)); } private static class TryFinally extends PendingTransaction { public final int tryNumber; public TryFinally(int tryNumber) { this.tryNumber = tryNumber; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to try begin proxy command is: // PushExpressionCommand(push try finally to proxy) followed by: // int: try id connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_FINALLY_EXPRESSION_VALUE); connection.pushInt(tryNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryFinallyClauseToProxy(int) */ protected void pushTryFinallyClauseToProxy(int tryNumber) { addPendingTransaction(new TryFinally(tryNumber)); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryEndToProxy(int) */ protected void pushTryEndToProxy(int tryNumber) { // This is a little tricky. We need to find if there is nothing but try/catch/finally for this tryNumber on the pending // transactions up to the try begin, if there is nothing else, then we can throw the entire try away. That // means there was no code at all in any of the try/catch/finally blocks. int fromTop = 0; while (true) { PendingTransaction topEntry = getPendingEntryFromTop(++fromTop); if (topEntry instanceof TryFinally) { if (((TryFinally) topEntry).tryNumber != tryNumber) break; // We met a finally that wasn't ours, so entire try group must be sent. } else if (topEntry instanceof TryCatch) { if (((TryCatch) topEntry).tryNumber != tryNumber) break; // We met a catch that wasn't ours, so entire try group must be sent. } else if (topEntry instanceof TryBegin) { if (((TryBegin) topEntry).tryNumber == tryNumber) { // We've met our try begin, and nothing but empty catch/finally in between, so the entire group can be thrown away popPendingEntry(fromTop); return; } else break; // We've hit a try begin that wasn't ours, so the entire try group must be sent. } else break; // We've hit something other than our try group, so process everything. } processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to try begin proxy command is: // PushExpressionCommand(push try end to proxy) followed by: // int: try id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.TRY_END_EXPRESSION_VALUE); connection.pushInt(tryNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushThrowToProxy() */ protected void pushThrowToProxy() { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to try begin proxy command is: // PushExpressionCommand(push throw to proxy) connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.THROW_EXPRESSION_VALUE); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushRethrowToProxy(int) */ protected void pushRethrowToProxy(int tryNumber) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to rethow proxy command is: // PushExpressionCommand(push rethrow to proxy) // int: try id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.RETHROW_EXPRESSION_VALUE); connection.pushInt(tryNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushBeanTypeToProxy(org.eclipse.jem.internal.proxy.core.IBeanTypeExpressionProxy) */ protected void pushBeanTypeToProxy(IBeanTypeExpressionProxy proxy) { // Push beantype to proxy is sent out of sequence without respect to where in expression we are, // so no need to handle pending transactions at this point. They would not affect the result // of this call. IREMExpressionConnection connection = getConnection(); try { // Format of push to beanType proxy command is: // PushExpressionCommand(push bean type expression proxy) // int: proxy id // string: typename connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_BEANTYPE_EXPRESSIONPROXY_EXPRESSION_VALUE); REMBeanTypeExpressionProxy ep = (REMBeanTypeExpressionProxy) proxy; connection.pushInt(ep.getProxyID()); connection.pushString(ep.getTypeName()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushMethodToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy, org.eclipse.jem.internal.proxy.core.IProxyBeanType, java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType[]) */ protected void pushMethodToProxy(ExpressionProxy proxy, IProxyBeanType declaringType, String methodName, IProxyBeanType[] parameterTypes) { // Push method to proxy is sent out of sequence without respect to where in expression we are, // so no need to handle pending transactions at this point. They would not affect the result // of this call. IREMExpressionConnection connection = getConnection(); try { // Format of push to method proxy command is: // PushExpressionCommand(push method type expression proxy) // int: proxy id // ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type // string: method name // int: number of parameter types // ValueObject(s): containing the rendered bean type proxy or the expression proxy for the parameter types. connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_METHOD_EXPRESSIONPROXY_EXPRESSION_VALUE); connection.pushInt(proxy.getProxyID()); fillProxy(declaringType, workerValue); connection.pushValueObject(workerValue); connection.pushString(methodName); if (parameterTypes == null || parameterTypes.length == 0) connection.pushInt(0); else { connection.pushInt(parameterTypes.length); for (int i = 0; i < parameterTypes.length; i++) { fillProxy(parameterTypes[i], workerValue); connection.pushValueObject(workerValue); } } } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushFieldToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy, org.eclipse.jem.internal.proxy.core.IProxyBeanType, java.lang.String) */ protected void pushFieldToProxy(ExpressionProxy proxy, IProxyBeanType declaringType, String fieldName) { // Push field to proxy is sent out of sequence without respect to where in expression we are, // so no need to handle pending transactions at this point. They would not affect the result // of this call. IREMExpressionConnection connection = getConnection(); try { // Format of push to field proxy command is: // PushExpressionCommand(push field type expression proxy) // int: proxy id // ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type // string: field name connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_FIELD_EXPRESSIONPROXY_EXPRESSION_VALUE); connection.pushInt(proxy.getProxyID()); fillProxy(declaringType, workerValue); connection.pushValueObject(workerValue); connection.pushString(fieldName); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } /** * Get the map of IProxyMethods for a beantype. Meant to be used only in conjunction with REMProxyConstants. * It is here so the REMProxyConstants can store pending proxies per expression. * * @param beanType * @return * * @since 1.1.0 */ public Map getMethods(IProxyBeanType beanType) { if (methodsCache == null) methodsCache = new HashMap(); Map methods = (Map) methodsCache.get(beanType.getTypeName()); if(methods == null){ methods = new HashMap(20); methodsCache.put(beanType.getTypeName(),methods); } return methods; } /** * Get the map of IProxyFields for a beantype. Meant to be used only in conjunction with REMProxyConstants. * It is here so the REMProxyConstants can store pending proxies per expression. * * @param beanType * @return * * @since 1.1.0 */ public Map getFields(IProxyBeanType beanType) { if (fieldsCache == null) fieldsCache = new HashMap(); Map fields = (Map) fieldsCache.get(beanType.getTypeName()); if(fields == null){ fields = new HashMap(20); fieldsCache.put(beanType.getTypeName(),fields); } return fields; } /** * Get the map of IProxyBeanTypes for a beantype name. Meant to be used only in conjunction with REMSgtandardBeanTypeFactory. * It is here so the REMStandardBeanTypeFactory can store pending proxies per expression. * * @param beanType * @return * * @since 1.1.0 */ public IProxyBeanType getBeanType(String beanTypeName) { if (beanTypeCache == null) beanTypeCache = new HashMap(); return (IProxyBeanType) beanTypeCache.get(beanTypeName); } /** * Add the beantype expression proxy to the map of bean type expression proxies. Used in conjunction with REMStandardBeanTypeFactory. * It is here so the REMStandardBeanTypeFactory can store pending proxies per expression. * @param beanTypeName * @param beantype * * @since 1.1.0 */ public void addBeanType(String beanTypeName, IProxyBeanType beantype) { beanTypeCache.put(beanTypeName, beantype); } /** * Remove the beantype expression proxy from the map. This is called because there was a rollback due to an endmark. * @param beanTypeName * * @since 1.1.0 */ public void removeBeanType(String beanTypeName) { beanTypeCache.remove(beanTypeName); } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushIfTestToProxy() */ protected void pushIfTestToProxy() { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push if test to proxy command is: // PushExpressionCommand(push if test to proxy) connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.IF_TEST_EXPRESSION_VALUE); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushIfElseToProxy(org.eclipse.jem.internal.proxy.initParser.tree.InternalIfElseOperandType) */ protected void pushIfElseToProxy(InternalIfElseOperandType clauseType) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push conditional to proxy command is: // PushExpressionCommand(push if/else clause to proxy) followed by: // byte: clause type connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.IF_ELSE_EXPRESSION_VALUE); connection.pushByte((byte) clauseType.getValue()); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.core.Expression#pushNewInstanceToProxy(java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType) */ protected void pushNewInstanceToProxy(String initializationString, IProxyBeanType resultType) { processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push new instance from initstring to proxy command is: // PushExpressionCommand(push new instance to proxy) followed by: // string: init string // ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.NEW_INSTANCE_VALUE); connection.pushString(initializationString); fillProxy(resultType, workerValue); connection.pushValueObject(workerValue); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); if (!e.isRecoverable()) { connection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } } private static class Mark extends PendingTransaction { public int markID; public Mark(int markID) { this.markID = markID; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push mark to proxy command is: // PushExpressionCommand(push mark to proxy) followed by: // int: markID connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.MARK_VALUE); connection.pushInt(markID); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } protected void pushMarkToProxy(int markID) { addPendingTransaction(new Mark(markID)); } protected void pushEndmarkToProxy(int markID, boolean restore) { // See if the top pending transaction is now Mark(markID). If it is, then this transaction and the mark begin // can be thrown away because they are an empty block. PendingTransaction topEntry = getPendingEntryFromTop(1); if (topEntry instanceof Mark && ((Mark) topEntry).markID == markID) { popPendingEntry(1); return; } processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push end mark to proxy command is: // PushExpressionCommand(push end mark to proxy) followed by: // int: markID // boolean: restore connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ENDMARK_VALUE); connection.pushInt(markID); connection.pushBoolean(restore); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } // This is the expression processor controller used to transfer. // This is the guy that maintains continuity of the transaction as // it is passed from one connection to another. protected IBeanProxy expressionProcesserController; protected void pushBeginTransferThreadToProxy() throws ThrowableProxy { // If the controller is not null, that means we had already requested a transfer // but had not used it in this thread so there is no need to do anything. It // will be handled when switching back to the other thread. // If the connection is null, no need to do anything since there is no connection // to transfer. if (connection != null && expressionProcesserController == null) { IREMExpressionConnection lclConnection = getConnection(); markInTransaction(lclConnection); try { workerValue.set(); lclConnection.transferExpression(getREMExpressionID(), workerValue); expressionProcesserController = getREMBeanProxyFactory().getBeanProxy(workerValue); getREMRegistry().returnConnection(lclConnection); this.connection = null; } catch (CommandException e) { ProxyPlugin.getPlugin().getLogger().log(e); if (!e.isRecoverable()) { lclConnection.close(); throwIllegalStateException(COMMAND_EXCEPTION_MSG); } } finally { markEndTransaction(lclConnection); } } } protected void pushTransferThreadToProxy() { // Don't need to do anything. The next time we need to push data across, we will get a connection and the getConnection() // will hook up the expression processor controller for us. This way if nothing happens in this thread then we won't // waste communication time on it. } private static class SubexpressionBegin extends PendingTransaction { public int subexpressionNumber; public SubexpressionBegin(int subexpressionNumber) { this.subexpressionNumber = subexpressionNumber; } public void pushTransaction(REMExpression remExpression) { IREMExpressionConnection connection = remExpression.getConnection(); try { // Format of push to subexpression begin proxy command is: // PushExpressionCommand(push subexpression begin proxy to proxy) followed by: // int: subexpression id connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.SUBEXPRESSION_BEGIN_EXPRESSION_VALUE); connection.pushInt(subexpressionNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); remExpression.markInvalid(e.getLocalizedMessage()); remExpression.throwIllegalStateException(IO_EXCEPTION_MSG); } } } protected void pushSubexpressionBeginToProxy(int subexpressionNumber) { addPendingTransaction(new SubexpressionBegin(subexpressionNumber)); } protected void pushSubexpressionEndToProxy(int subexpressionNumber) { // See if the top pending transactions is SubexpressionBegin(subexpressionNumber). If it is then the SubexpressionBegin can be thrown away. PendingTransaction topEntry = getPendingEntryFromTop(1); if (topEntry instanceof SubexpressionBegin && ((SubexpressionBegin) topEntry).subexpressionNumber == subexpressionNumber) { popPendingEntry(1); return; } processPendingTransactions(); IREMExpressionConnection connection = getConnection(); try { // Format of push to block end proxy command is: // PushExpressionCommand(push subexpression end proxy to proxy) followed by: // int: subexpression id connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.SUBEXPRESSION_END_EXPRESSION_VALUE); connection.pushInt(subexpressionNumber); } catch (IOException e) { connection.close(); ProxyPlugin.getPlugin().getLogger().log(e); markInvalid(e.getLocalizedMessage()); throwIllegalStateException(IO_EXCEPTION_MSG); } } }