public Object invokeHome(Invocation mi) throws Exception { EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi.getEnterpriseContext(); Transaction tx = mi.getTransaction(); Object rtn = getNext().invokeHome(mi); // An anonymous context was sent in, so if it has an id it is a real instance now if (ctx.getId() != null) { // it doesn't need to be read, but it might have been changed from the db already. ctx.setValid(true); if (tx != null) { BeanLock lock = container.getLockManager().getLock(ctx.getCacheKey()); try { lock.schedule(mi); register(ctx, tx); // Set tx lock.endInvocation(mi); } finally { container.getLockManager().removeLockRef(lock.getId()); } } } return rtn; }
/** Do some validation of the incoming parameters */ protected void validateArguments(Invocation mi) { if (mi.getType() == InvocationType.REMOTE) { Object[] params = mi.getArguments(); for (int i = 0; i < params.length; i++) { Object obj = params[i]; if (obj instanceof TimerHandle) throw new IllegalArgumentException("Cannot pass TimerHandle through remote interface"); } } }
/** Do some validation of the return value */ protected Object validateReturnValue(Invocation mi, Object retValue) { if (mi.getType() == InvocationType.REMOTE) { if (retValue instanceof TimerHandle) throw new IllegalArgumentException("Cannot return TimerHandle from remote interface"); } return retValue; }
public Object invoke(Invocation mi) throws Exception { // We are going to work with the context a lot EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi.getEnterpriseContext(); // The Tx coming as part of the Method Invocation Transaction tx = mi.getTransaction(); if (log.isTraceEnabled()) log.trace("invoke called for ctx " + ctx + ", tx=" + tx); if (!ctx.isValid()) { container.getPersistenceManager().loadEntity(ctx); ctx.setValid(true); } // mark the context as read only if this is a readonly method and the context // was not already readonly boolean didSetReadOnly = false; if (!ctx.isReadOnly() && (container.isReadOnly() || container.getBeanMetaData().isMethodReadOnly(mi.getMethod()))) { ctx.setReadOnly(true); didSetReadOnly = true; } // So we can go on with the invocation // Invocation with a running Transaction try { if (tx != null && tx.getStatus() != Status.STATUS_NO_TRANSACTION) { // readonly does not synchronize, lock or belong with transaction. boolean isReadOnly = container.isReadOnly(); if (isReadOnly == false) { Method method = mi.getMethod(); if (method != null) isReadOnly = container.getBeanMetaData().isMethodReadOnly(method.getName()); } try { if (isReadOnly == false) { // register the wrapper with the transaction monitor (but only // register once). The transaction demarcation will trigger the // storage operations register(ctx, tx); } // Invoke down the chain Object retVal = getNext().invoke(mi); // Register again as a finder in the middle of a method // will de-register this entity, and then the rest of the method can // change fields which will never be stored if (isReadOnly == false) { // register the wrapper with the transaction monitor (but only // register once). The transaction demarcation will trigger the // storage operations register(ctx, tx); } // return the return value return retVal; } finally { // We were read-only and the context wasn't already synchronized, tidyup the cache if (isReadOnly && ctx.hasTxSynchronization() == false) { switch (commitOption) { // Keep instance active, but invalidate state case ConfigurationMetaData.B_COMMIT_OPTION: // Invalidate state (there might be other points of entry) ctx.setValid(false); break; // Invalidate everything AND Passivate instance case ConfigurationMetaData.C_COMMIT_OPTION: try { // FIXME: We cannot passivate here, because previous // interceptors work with the context, in particular // the re-entrance interceptor is doing lock counting // Just remove it from the cache if (ctx.getId() != null) container.getInstanceCache().remove(ctx.getId()); } catch (Exception e) { log.debug("Exception releasing context", e); } break; } } } } else { // No tx try { Object result = getNext().invoke(mi); // Store after each invocation -- not on exception though, or removal // And skip reads too ("get" methods) if (ctx.getId() != null && !container.isReadOnly()) { container.invokeEjbStore(ctx); container.storeEntity(ctx); } return result; } catch (Exception e) { // Exception - force reload on next call ctx.setValid(false); throw e; } finally { switch (commitOption) { // Keep instance active, but invalidate state case ConfigurationMetaData.B_COMMIT_OPTION: // Invalidate state (there might be other points of entry) ctx.setValid(false); break; // Invalidate everything AND Passivate instance case ConfigurationMetaData.C_COMMIT_OPTION: try { // Do not call release if getId() is null. This means that // the entity has been removed from cache. // release will schedule a passivation and this removed ctx // could be put back into the cache! // This is necessary because we have no lock, we // don't want to return an instance to the pool that is // being used if (ctx.getId() != null) container.getInstanceCache().remove(ctx.getId()); } catch (Exception e) { log.debug("Exception releasing context", e); } break; } } } } finally { // if we marked the context as read only we need to reset it if (didSetReadOnly) { ctx.setReadOnly(false); } } }
/** * Implementation of the server invoker handler interface. Will take the invocation request and * invoke down the interceptor chain. * * @param invocationReq * @return response of the invocation * @throws Throwable */ public Object invoke(InvocationRequest invocationReq) throws Throwable { Invocation invocation = (Invocation) invocationReq.getParameter(); Thread currentThread = Thread.currentThread(); ClassLoader oldCl = currentThread.getContextClassLoader(); ObjectName mbean = null; try { mbean = (ObjectName) Registry.lookup(invocation.getObjectName()); /** Clustering * */ long clientViewId = ((Long) invocation.getValue("CLUSTER_VIEW_ID")).longValue(); HATarget target = (HATarget) beanMap.get(invocation.getObjectName()); if (target == null) { // We could throw IllegalStateException but we have a race condition that could occur: // when we undeploy a bean, the cluster takes some time to converge // and to recalculate a new viewId and list of replicant for each HATarget. // Consequently, a client could own an up-to-date list of the replicants // (before the cluster has converged) and try to perform an invocation // on this node where the HATarget no more exist, thus receiving a // wrong exception and no failover is performed with an IllegalStateException // throw new GenericClusteringException( GenericClusteringException.COMPLETED_NO, "target is not/no more registered on this node"); } if (!target.invocationsAllowed()) { throw new GenericClusteringException( GenericClusteringException.COMPLETED_NO, "invocations are currently not allowed on this target"); } /** End Clustering * */ // The cl on the thread should be set in another interceptor Object obj = getServer() .invoke(mbean, "invoke", new Object[] {invocation}, Invocation.INVOKE_SIGNATURE); /** Clustering * */ HARMIResponse haResponse = new HARMIResponse(); if (clientViewId != target.getCurrentViewId()) { haResponse.newReplicants = new ArrayList(target.getReplicants()); haResponse.currentViewId = target.getCurrentViewId(); } haResponse.response = obj; /** End Clustering * */ return new MarshalledObject(haResponse); } catch (Exception e) { Throwable th = JMXExceptionDecoder.decode(e); if (log.isTraceEnabled()) { log.trace("Failed to invoke on mbean: " + mbean, th); } if (th instanceof Exception) { e = (Exception) th; } throw e; } finally { currentThread.setContextClassLoader(oldCl); Thread.interrupted(); // clear interruption because this thread may be pooled. } }