protected Object invalidateAcrossCluster( boolean synchronous, InvocationContext ctx, Object[] keys, boolean useFuture, final Object retvalForFuture) throws Throwable { if (!isLocalModeForced(ctx)) { // increment invalidations counter if statistics maintained incrementInvalidations(); final InvalidateCommand command = commandsFactory.buildInvalidateCommand(keys); if (log.isDebugEnabled()) log.debug("Cache [" + rpcManager.getTransport().getAddress() + "] replicating " + command); // voila, invalidated! if (useFuture) { NotifyingNotifiableFuture<Object> future = new NotifyingFutureImpl(retvalForFuture); rpcManager.broadcastRpcCommandInFuture(command, future); return future; } else { rpcManager.broadcastRpcCommand(command, synchronous, false); } } return retvalForFuture; }
private Cache getCache(String regionName, String typeKey, Properties properties) { TypeOverrides regionOverride = typeOverrides.get(regionName); if (!definedConfigurations.contains(regionName)) { String templateCacheName = null; Configuration regionCacheCfg = null; if (regionOverride != null) { if (log.isDebugEnabled()) log.debug("Cache region specific configuration exists: " + regionOverride); regionOverride = overrideStatisticsIfPresent(regionOverride, properties); regionCacheCfg = regionOverride.createInfinispanConfiguration(); String cacheName = regionOverride.getCacheName(); if (cacheName != null) // Region specific override with a given cache name templateCacheName = cacheName; else // Region specific override without cache name, so template cache name is generic for // data type. templateCacheName = typeOverrides.get(typeKey).getCacheName(); } else { // No region specific overrides, template cache name is generic for data type. templateCacheName = typeOverrides.get(typeKey).getCacheName(); regionCacheCfg = typeOverrides.get(typeKey).createInfinispanConfiguration(); } // Configure transaction manager regionCacheCfg = configureTransactionManager(regionCacheCfg, templateCacheName, properties); // Apply overrides manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg); definedConfigurations.add(regionName); } Cache cache = manager.getCache(regionName); if (!cache.getStatus().allowInvocations()) { cache.start(); } return createCacheWrapper(cache.getAdvancedCache()); }
private void invalidateAcrossCluster(boolean synchronous, Object[] keys, InvocationContext ctx) throws Throwable { // increment invalidations counter if statistics maintained incrementInvalidations(); final InvalidateCommand invalidateCommand = commandsFactory.buildInvalidateCommand(InfinispanCollections.<Flag>emptySet(), keys); if (log.isDebugEnabled()) log.debug("Cache [" + rpcManager.getAddress() + "] replicating " + invalidateCommand); ReplicableCommand command = invalidateCommand; if (ctx.isInTxScope()) { TxInvocationContext txCtx = (TxInvocationContext) ctx; // A Prepare command containing the invalidation command in its 'modifications' list is sent // to the remote nodes // so that the invalidation is executed in the same transaction and locks can be acquired and // released properly. // This is 1PC on purpose, as an optimisation, even if the current TX is 2PC. // If the cache uses 2PC it's possible that the remotes will commit the invalidation and the // originator rolls back, // but this does not impact consistency and the speed benefit is worth it. command = commandsFactory.buildPrepareCommand( txCtx.getGlobalTransaction(), Collections.<WriteCommand>singletonList(invalidateCommand), true); } rpcManager.invokeRemotely(null, command, rpcManager.getDefaultRpcOptions(synchronous)); }
private void shutDownGracefully() { if (log.isDebugEnabled()) log.debugf( "Wait for on-going transactions to finish for %s.", Util.prettyPrintTime( configuration.transaction().cacheStopTimeout(), TimeUnit.MILLISECONDS)); long failTime = currentMillisFromNanotime() + configuration.transaction().cacheStopTimeout(); boolean txsOnGoing = areTxsOnGoing(); while (txsOnGoing && currentMillisFromNanotime() < failTime) { try { Thread.sleep(30); txsOnGoing = areTxsOnGoing(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); if (clustered) { log.debugf( "Interrupted waiting for on-going transactions to finish. %s local transactions and %s remote transactions", localTransactions.size(), remoteTransactions.size()); } else { log.debugf( "Interrupted waiting for %s on-going transactions to finish.", localTransactions.size()); } } } if (txsOnGoing) { log.unfinishedTransactionsRemain( localTransactions == null ? 0 : localTransactions.size(), remoteTransactions == null ? 0 : remoteTransactions.size()); } else { log.debug("All transactions terminated"); } }
void stop(EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) { if (log.isDebugEnabled()) log.debugf("Stopping cache %s on %s", getName(), getCacheManager().getAddress()); // Create invocation context to pass flags createNonTxInvocationContext(explicitFlags, explicitClassLoader); componentRegistry.stop(); }
@ManagedOperation(description = "Starts the cache.") @Operation(displayName = "Starts cache.") public void start() { componentRegistry.start(); defaultLifespan = config.getExpirationLifespan(); defaultMaxIdleTime = config.getExpirationMaxIdle(); if (log.isDebugEnabled()) log.debugf("Started cache %s on %s", getName(), getCacheManager().getAddress()); }
/** {@inheritDoc} */ public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException { if (log.isDebugEnabled()) log.debug("Building timestamps cache region [" + regionName + "]"); Cache cache = getCache(regionName, TIMESTAMPS_KEY, properties); CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache); TimestampsRegionImpl region = createTimestampsRegion(cacheAdapter, regionName); region.start(); return region; }
@Override @SuppressWarnings("unchecked") public synchronized void futureDone(Future<Object> objectFuture) { SenderContainer sc = futures.get(objectFuture); if (sc.processed) { // This can happen - it is a race condition in JGroups' NotifyingFuture.setListener() where // a listener // could be notified twice. if (trace) log.tracef( "Not processing callback; already processed callback for sender %s", sc.address); } else { sc.processed = true; Address sender = sc.address; boolean done = false; try { if (retval == null) { Object response = objectFuture.get(); if (trace) log.tracef("Received response: %s from %s", response, sender); filter.isAcceptable(response, sender); if (!filter.needMoreResponses()) { retval = new RspList(Collections.singleton(new Rsp(sender, response))); done = true; // TODO cancel other tasks? } } else { if (trace) log.tracef( "Skipping response from %s since a valid response for this request has already been received", sender); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof org.jgroups.SuspectedException) { // Do not set the exception field, RpcException should be thrown if there is no other // valid response return; } else if (cause instanceof org.jgroups.TimeoutException) { exception = new TimeoutException("Timeout!", e); } else if (cause instanceof Exception) { exception = (Exception) cause; } else { exception = new CacheException("Caught a throwable", cause); } if (log.isDebugEnabled()) log.debugf("Caught exception from sender %s: %s", sender, exception); } finally { expectedResponses--; if (expectedResponses == 0 || done) { this.notify(); // make sure to awake waiting thread, but avoid unnecessary wakeups! } } } }
/** {@inheritDoc} */ public EntityRegion buildEntityRegion( String regionName, Properties properties, CacheDataDescription metadata) throws CacheException { if (log.isDebugEnabled()) log.debug("Building entity cache region [" + regionName + "]"); Cache cache = getCache(regionName, ENTITY_KEY, properties); CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache); EntityRegionImpl region = new EntityRegionImpl(cacheAdapter, regionName, metadata, transactionManager, this); region.start(); return region; }
private static void removeInMemoryData(Cache cache) { EmbeddedCacheManager mgr = cache.getCacheManager(); Address a = mgr.getAddress(); String str; if (a == null) str = "a non-clustered cache manager"; else str = "a cache manager at address " + a; log.debugf("Cleaning data for cache '%s' on %s", cache.getName(), str); DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class); if (log.isDebugEnabled()) log.debugf("removeInMemoryData(): dataContainerBefore == %s", dataContainer.entrySet(null)); dataContainer.clear(); if (log.isDebugEnabled()) log.debugf("removeInMemoryData(): dataContainerAfter == %s", dataContainer.entrySet(null)); L1GMUContainer l1GMUContainer = TestingUtil.extractComponent(cache, L1GMUContainer.class); if (log.isDebugEnabled()) log.debugf( "removeInMemoryData(): l1GMUContainerBefore == %s", l1GMUContainer.chainToString()); l1GMUContainer.clear(); if (log.isDebugEnabled()) log.debugf("removeInMemoryData(): l1GMUContainerAfter == %s", l1GMUContainer.chainToString()); }
private void updateTransactionVersion(InvocationContext context) { if (!context.isInTxScope() && !context.isOriginLocal()) { return; } if (context instanceof SingleKeyNonTxInvocationContext) { if (log.isDebugEnabled()) { log.debugf( "Received a SingleKeyNonTxInvocationContext... This should be a single read operation"); } return; } TxInvocationContext txInvocationContext = (TxInvocationContext) context; List<EntryVersion> entryVersionList = new LinkedList<EntryVersion>(); entryVersionList.add(txInvocationContext.getTransactionVersion()); if (log.isTraceEnabled()) { log.tracef( "[%s] Keys read in this command: %s", txInvocationContext.getGlobalTransaction().prettyPrint(), txInvocationContext.getKeysReadInCommand()); } for (InternalGMUCacheEntry internalGMUCacheEntry : txInvocationContext.getKeysReadInCommand().values()) { Object key = internalGMUCacheEntry.getKey(); boolean local = cll.localNodeIsOwner(key); if (log.isTraceEnabled()) { log.tracef( "[%s] Analyze entry [%s]: local?=%s", txInvocationContext.getGlobalTransaction().prettyPrint(), internalGMUCacheEntry, local); } if (txInvocationContext.hasModifications() && !internalGMUCacheEntry.isMostRecent()) { throw new CacheException("Read-Write transaction read an old value and should rollback"); } if (internalGMUCacheEntry.getMaximumTransactionVersion() != null) { entryVersionList.add(internalGMUCacheEntry.getMaximumTransactionVersion()); } txInvocationContext.getCacheTransaction().addReadKey(key); if (local) { txInvocationContext.setAlreadyReadOnThisNode(true); txInvocationContext.addReadFrom(cll.getAddress()); } } if (entryVersionList.size() > 1) { EntryVersion[] txVersionArray = new EntryVersion[entryVersionList.size()]; txInvocationContext.setTransactionVersion( versionGenerator.mergeAndMax(entryVersionList.toArray(txVersionArray))); } }
/** * Returns a named sub-element of the current element passed in. * * <p>None of the parameters should be null - otherwise the method may throw a * NullPointerException. * * @param element - element to search through. * @param subElementName - the name of a sub element to look for * @return the first matching sub element, if found, or null otherwise. */ public static Element getSubElement(Element element, String subElementName) { NodeList nl = element.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node.getNodeType() == Node.ELEMENT_NODE && subElementName.equals(((Element) node).getTagName())) { return (Element) node; } } if (log.isDebugEnabled()) log.debugf("getSubElement(): Does not exist for %s", subElementName); return null; }
final void putForExternalRead( K key, V value, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) { Transaction ongoingTransaction = null; try { ongoingTransaction = getOngoingTransaction(); if (ongoingTransaction != null) transactionManager.suspend(); EnumSet<Flag> flags = EnumSet.of( FAIL_SILENTLY, FORCE_ASYNCHRONOUS, ZERO_LOCK_ACQUISITION_TIMEOUT, PUT_FOR_EXTERNAL_READ); if (explicitFlags != null && !explicitFlags.isEmpty()) { flags.addAll(explicitFlags); } // if the entry exists then this should be a no-op. putIfAbsent( key, value, defaultLifespan, TimeUnit.MILLISECONDS, defaultMaxIdleTime, TimeUnit.MILLISECONDS, flags, explicitClassLoader); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Caught exception while doing putForExternalRead()", e); } finally { try { if (ongoingTransaction != null) transactionManager.resume(ongoingTransaction); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Had problems trying to resume a transaction after putForExternalRead()", e); } } }
/** {@inheritDoc} */ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException { if (log.isDebugEnabled()) log.debug("Building query results cache region [" + regionName + "]"); String cacheName = typeOverrides.get(QUERY_KEY).getCacheName(); // If region name is not default one, lookup a cache for that region name if (!regionName.equals("org.hibernate.cache.internal.StandardQueryCache")) cacheName = regionName; Cache cache = getCache(cacheName, QUERY_KEY, properties); CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache); QueryResultsRegionImpl region = new QueryResultsRegionImpl(cacheAdapter, regionName, properties, transactionManager, this); region.start(); return region; }
@Override public void endStateTransfer(String sendingSite) { if (debug) { log.debugf("Ending state transfer from %s", sendingSite); } String currentSendingSite = this.sendingSite.get(); if (sendingSite == null || sendingSite.equals(currentSendingSite)) { this.sendingSite.set(null); commitManager.stopTrack(PUT_FOR_X_SITE_STATE_TRANSFER); } else { if (log.isDebugEnabled()) { log.debugf( "Received an end request from a non-sender site. Expects %s but got %s", currentSendingSite, sendingSite); } } }
/** * returns the request object list for the {@code member} * * @param member the destination member * @return the request object list. It can be empty if no requests are necessary */ public final synchronized ObjectRequest getObjectRequestForAddress(Address member) { int addressIndex = clusterSnapshot.indexOf(member); if (addressIndex == -1) { log.warnf( "Trying to get Object Requests to send to %s but it does not exists in cluster snapshot %s", member, clusterSnapshot); return new ObjectRequest(null, null); } ObjectRequest request = accessesByPrimaryOwner[addressIndex].toObjectRequest(); if (log.isInfoEnabled()) { log.debugf( "Getting request list for %s. Request is %s", member, request.toString(log.isDebugEnabled())); } return request; }
/** * validates the read set and returns the prepare version from the commit queue * * @param ctx the context * @param command the prepare command * @throws InterruptedException if interrupted */ protected void performValidation(TxInvocationContext ctx, GMUPrepareCommand command) throws InterruptedException { boolean hasToUpdateLocalKeys = hasLocalKeysToUpdate(command.getModifications()); boolean isReadOnly = command.getModifications().length == 0; if (!isReadOnly) { cll.performReadSetValidation(ctx, command); if (hasToUpdateLocalKeys) { transactionCommitManager.prepareTransaction(ctx.getCacheTransaction()); } else { transactionCommitManager.prepareReadOnlyTransaction(ctx.getCacheTransaction()); } } if (log.isDebugEnabled()) { log.debugf( "Transaction %s can commit on this node. Prepare Version is %s", command.getGlobalTransaction().prettyPrint(), ctx.getTransactionVersion()); } }
@Override public void handleUnmarshallingException(Throwable problem, Class<?> subjectClass) { if (log.isDebugEnabled()) { StringBuilder builder = new StringBuilder(); ClassLoader cl = subjectClass.getClassLoader(); builder.append("classloader hierarchy:"); ClassLoader parent = cl; while (parent != null) { if (parent.equals(cl)) { builder.append("\n\t\t-> type classloader = ").append(parent); } else { builder.append("\n\t\t-> parent classloader = ").append(parent); } URL[] urls = getClassLoaderURLs(parent); if (urls != null) { for (URL u : urls) builder.append("\n\t\t->...").append(u); } parent = parent.getParent(); } TraceInformation.addUserInformation(problem, builder.toString()); } }
/** * @author Mircea Markus * @since 5.2 */ public class BackupReceiverImpl implements BackupReceiver { private static final Log log = LogFactory.getLog(BackupReceiverImpl.class); private static final boolean trace = log.isDebugEnabled(); private final Cache<Object, Object> cache; private final BackupCacheUpdater siteUpdater; public BackupReceiverImpl(Cache<Object, Object> cache) { this.cache = cache; siteUpdater = new BackupCacheUpdater(cache); } @Override public Cache getCache() { return cache; } @Override public Object handleRemoteCommand(VisitableCommand command) throws Throwable { return command.acceptVisitor(null, siteUpdater); } @Override public void handleStateTransferControl(XSiteStateTransferControlCommand command) throws Exception { XSiteStateTransferControlCommand invokeCommand = command; if (!command.getCacheName().equals(cache.getName())) { // copy if the cache name is different invokeCommand = command.copyForCache(cache.getName()); } invokeCommand.setSiteName(command.getOriginSite()); invokeRemotelyInLocalSite(invokeCommand); } @Override public void handleStateTransferState(XSiteStatePushCommand cmd) throws Exception { // split the state and forward it to the primary owners... if (!cache.getStatus().allowInvocations()) { throw new CacheException("Cache is stopping or terminated: " + cache.getStatus()); } final ClusteringDependentLogic clusteringDependentLogic = cache .getAdvancedCache() .getComponentRegistry() .getComponent(ClusteringDependentLogic.class); final Map<Address, List<XSiteState>> primaryOwnersChunks = new HashMap<>(); if (trace) { log.tracef("Received X-Site state transfer '%s'. Splitting by primary owner.", cmd); } for (XSiteState state : cmd.getChunk()) { final Address primaryOwner = clusteringDependentLogic.getPrimaryOwner(state.key()); List<XSiteState> primaryOwnerList = primaryOwnersChunks.get(primaryOwner); if (primaryOwnerList == null) { primaryOwnerList = new LinkedList<>(); primaryOwnersChunks.put(primaryOwner, primaryOwnerList); } primaryOwnerList.add(state); } final List<XSiteState> localChunks = primaryOwnersChunks.remove(clusteringDependentLogic.getAddress()); final List<StatePushTask> tasks = new ArrayList<>(primaryOwnersChunks.size()); for (Map.Entry<Address, List<XSiteState>> entry : primaryOwnersChunks.entrySet()) { if (entry.getValue() == null || entry.getValue().isEmpty()) { continue; } if (trace) { log.tracef("Node '%s' will apply %s", entry.getKey(), entry.getValue()); } StatePushTask task = new StatePushTask(entry.getValue(), entry.getKey(), cache); tasks.add(task); task.executeRemote(); } // help gc. this is safe because the chunks was already sent primaryOwnersChunks.clear(); if (trace) { log.tracef( "Local node '%s' will apply %s", cache.getAdvancedCache().getRpcManager().getAddress(), localChunks); } if (localChunks != null) { LocalInvocation.newInstanceFromCache(cache, newStatePushCommand(cache, localChunks)).call(); // help gc localChunks.clear(); } if (trace) { log.tracef("Waiting for the remote tasks..."); } while (!tasks.isEmpty()) { for (Iterator<StatePushTask> iterator = tasks.iterator(); iterator.hasNext(); ) { awaitRemoteTask(cache, iterator.next()); iterator.remove(); } } // the put operation can fail silently. check in the end and it is better to resend the chunk // than to lose keys. if (!cache.getStatus().allowInvocations()) { throw new CacheException("Cache is stopping or terminated: " + cache.getStatus()); } } private static void awaitRemoteTask(Cache<?, ?> cache, StatePushTask task) throws Exception { try { if (trace) { log.tracef("Waiting reply from %s", task.address); } Map<Address, Response> responseMap = task.awaitRemote(); if (trace) { log.tracef("Response received is %s", responseMap); } if (responseMap.size() > 1 || !responseMap.containsKey(task.address)) { throw new IllegalStateException("Shouldn't happen. Map is " + responseMap); } Response response = responseMap.get(task.address); if (response == CacheNotFoundResponse.INSTANCE) { if (trace) { log.tracef("Cache not found in node '%s'. Retrying locally!", task.address); } if (!cache.getStatus().allowInvocations()) { throw new CacheException("Cache is stopping or terminated: " + cache.getStatus()); } task.executeLocal(); } } catch (Exception e) { if (!cache.getStatus().allowInvocations()) { throw new CacheException("Cache is stopping or terminated: " + cache.getStatus()); } if (cache.getAdvancedCache().getRpcManager().getMembers().contains(task.address)) { if (trace) { log.tracef(e, "An exception was sent by %s. Retrying!", task.address); } task.executeRemote(); // retry! } else { if (trace) { log.tracef(e, "An exception was sent by %s. Retrying locally!", task.address); } // if the node left the cluster, we apply the missing state. This avoids the site provider // to re-send the // full chunk. task.executeLocal(); } } } private static XSiteStatePushCommand newStatePushCommand( Cache<?, ?> cache, List<XSiteState> stateList) { CommandsFactory commandsFactory = cache.getAdvancedCache().getComponentRegistry().getCommandsFactory(); return commandsFactory.buildXSiteStatePushCommand( stateList.toArray(new XSiteState[stateList.size()])); } private Map<Address, Response> invokeRemotelyInLocalSite(CacheRpcCommand command) throws Exception { final RpcManager rpcManager = cache.getAdvancedCache().getRpcManager(); final NotifyingNotifiableFuture<Map<Address, Response>> remoteFuture = new NotifyingFutureImpl<>(); final Map<Address, Response> responseMap = new HashMap<>(); rpcManager.invokeRemotelyInFuture( remoteFuture, null, command, rpcManager.getDefaultRpcOptions(true, false)); responseMap.put( rpcManager.getAddress(), LocalInvocation.newInstanceFromCache(cache, command).call()); //noinspection unchecked responseMap.putAll(remoteFuture.get()); return responseMap; } public static final class BackupCacheUpdater extends AbstractVisitor { private static Log log = LogFactory.getLog(BackupCacheUpdater.class); private final ConcurrentMap<GlobalTransaction, GlobalTransaction> remote2localTx; private final AdvancedCache<Object, Object> backupCache; BackupCacheUpdater(Cache<Object, Object> backup) { // ignore return values on the backup this.backupCache = backup.getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES, Flag.SKIP_XSITE_BACKUP); this.remote2localTx = new ConcurrentHashMap<>(); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { log.tracef("Processing a remote put %s", command); if (command.isConditional()) { return backupCache.putIfAbsent(command.getKey(), command.getValue(), command.getMetadata()); } return backupCache.put(command.getKey(), command.getValue(), command.getMetadata()); } @Override public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable { if (command.isConditional()) { return backupCache.remove(command.getKey(), command.getValue()); } return backupCache.remove(command.getKey()); } @Override public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable { if (command.isConditional() && command.getOldValue() != null) { return backupCache.replace( command.getKey(), command.getOldValue(), command.getNewValue(), command.getMetadata()); } return backupCache.replace(command.getKey(), command.getNewValue(), command.getMetadata()); } @Override public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable { Metadata metadata = command.getMetadata(); backupCache.putAll( command.getMap(), metadata.lifespan(), TimeUnit.MILLISECONDS, metadata.maxIdle(), TimeUnit.MILLISECONDS); return null; } @Override public Object visitClearCommand(InvocationContext ctx, ClearCommand command) throws Throwable { backupCache.clear(); return null; } @Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { boolean isTransactional = isTransactional(); if (isTransactional) { // Sanity check -- if the remote tx doesn't have modifications, it never should have been // propagated! if (!command.hasModifications()) { throw new IllegalStateException("TxInvocationContext has no modifications!"); } replayModificationsInTransaction(command, command.isOnePhaseCommit()); } else { replayModifications(command); } return null; } private boolean isTransactional() { return backupCache.getCacheConfiguration().transaction().transactionMode() == TransactionMode.TRANSACTIONAL; } @Override public Object visitCommitCommand(TxInvocationContext ctx, CommitCommand command) throws Throwable { if (!isTransactional()) { log.cannotRespondToCommit(command.getGlobalTransaction(), backupCache.getName()); } else { log.tracef("Committing remote transaction %s", command.getGlobalTransaction()); completeTransaction(command.getGlobalTransaction(), true); } return null; } @Override public Object visitRollbackCommand(TxInvocationContext ctx, RollbackCommand command) throws Throwable { if (!isTransactional()) { log.cannotRespondToRollback(command.getGlobalTransaction(), backupCache.getName()); } else { log.tracef("Rolling back remote transaction %s", command.getGlobalTransaction()); completeTransaction(command.getGlobalTransaction(), false); } return null; } private void completeTransaction(GlobalTransaction globalTransaction, boolean commit) throws Throwable { TransactionTable txTable = txTable(); GlobalTransaction localTxId = remote2localTx.remove(globalTransaction); if (localTxId == null) { throw new CacheException( "Couldn't find a local transaction corresponding to remote transaction " + globalTransaction); } LocalTransaction localTx = txTable.getLocalTransaction(localTxId); if (localTx == null) { throw new IllegalStateException("Local tx not found but present in the tx table!"); } TransactionManager txManager = txManager(); txManager.resume(localTx.getTransaction()); if (commit) { txManager.commit(); } else { txManager.rollback(); } } private void replayModificationsInTransaction(PrepareCommand command, boolean onePhaseCommit) throws Throwable { TransactionManager tm = txManager(); boolean replaySuccessful = false; try { tm.begin(); replayModifications(command); replaySuccessful = true; } finally { LocalTransaction localTx = txTable().getLocalTransaction(tm.getTransaction()); if (localTx != null) { // possible for the tx to be null if we got an exception during applying // modifications localTx.setFromRemoteSite(true); if (onePhaseCommit) { if (replaySuccessful) { log.tracef( "Committing remotely originated tx %s as it is 1PC", command.getGlobalTransaction()); tm.commit(); } else { log.tracef("Rolling back remotely originated tx %s", command.getGlobalTransaction()); tm.rollback(); } } else { // Wait for a remote commit/rollback. remote2localTx.put(command.getGlobalTransaction(), localTx.getGlobalTransaction()); tm.suspend(); } } } } private TransactionManager txManager() { return backupCache.getAdvancedCache().getTransactionManager(); } public TransactionTable txTable() { return backupCache.getComponentRegistry().getComponent(TransactionTable.class); } private void replayModifications(PrepareCommand command) throws Throwable { for (WriteCommand c : command.getModifications()) { c.acceptVisitor(null, this); } } } private static class StatePushTask { private final List<XSiteState> chunk; private final Address address; private final Cache<?, ?> cache; private volatile Future<Map<Address, Response>> remoteFuture; private StatePushTask(List<XSiteState> chunk, Address address, Cache<?, ?> cache) { this.chunk = chunk; this.address = address; this.cache = cache; } public void executeRemote() { final RpcManager rpcManager = cache.getAdvancedCache().getRpcManager(); NotifyingNotifiableFuture<Map<Address, Response>> future = new NotifyingFutureImpl<>(); remoteFuture = future; rpcManager.invokeRemotelyInFuture( future, Collections.singletonList(address), newStatePushCommand(cache, chunk), rpcManager.getDefaultRpcOptions(true)); } public Response executeLocal() throws Exception { return LocalInvocation.newInstanceFromCache(cache, newStatePushCommand(cache, chunk)).call(); } public Map<Address, Response> awaitRemote() throws Exception { Future<Map<Address, Response>> future = remoteFuture; if (future == null) { throw new NullPointerException("Should not happen!"); } return future.get(); } } }
/** * It contains the logic needed to consume the state sent from other site. * * @author Pedro Ruivo * @since 7.0 */ public class XSiteStateConsumerImpl implements XSiteStateConsumer { private static final EnumSet<Flag> STATE_TRANSFER_PUT_FLAGS = EnumSet.of( PUT_FOR_X_SITE_STATE_TRANSFER, IGNORE_RETURN_VALUES, SKIP_REMOTE_LOOKUP, SKIP_XSITE_BACKUP); private static final Log log = LogFactory.getLog(XSiteStateConsumerImpl.class); private static final boolean trace = log.isTraceEnabled(); private static final boolean debug = log.isDebugEnabled(); private TransactionManager transactionManager; private InvocationContextFactory invocationContextFactory; private CommandsFactory commandsFactory; private InterceptorChain interceptorChain; private CommitManager commitManager; private AtomicReference<String> sendingSite = new AtomicReference<>(null); @Inject public void inject( TransactionManager transactionManager, InvocationContextFactory invocationContextFactory, CommandsFactory commandsFactory, InterceptorChain interceptorChain, CommitManager commitManager) { this.transactionManager = transactionManager; this.invocationContextFactory = invocationContextFactory; this.commandsFactory = commandsFactory; this.interceptorChain = interceptorChain; this.commitManager = commitManager; } @Override public void startStateTransfer(String sendingSite) { if (debug) { log.debugf("Starting state transfer. Receiving from %s", sendingSite); } if (this.sendingSite.compareAndSet(null, sendingSite)) { commitManager.startTrack(Flag.PUT_FOR_X_SITE_STATE_TRANSFER); } else { throw new CacheException("Already receiving state from " + this.sendingSite.get()); } } @Override public void endStateTransfer(String sendingSite) { if (debug) { log.debugf("Ending state transfer from %s", sendingSite); } String currentSendingSite = this.sendingSite.get(); if (sendingSite == null || sendingSite.equals(currentSendingSite)) { this.sendingSite.set(null); commitManager.stopTrack(PUT_FOR_X_SITE_STATE_TRANSFER); } else { if (log.isDebugEnabled()) { log.debugf( "Received an end request from a non-sender site. Expects %s but got %s", currentSendingSite, sendingSite); } } } @Override public void applyState(XSiteState[] chunk) throws Exception { if (debug) { log.debugf("Received state: %s keys", chunk.length); } if (transactionManager != null) { applyStateInTransaction(chunk); } else { applyStateInNonTransaction(chunk); } } @Override public String getSendingSiteName() { return sendingSite.get(); } private void applyStateInTransaction(XSiteState[] chunk) throws Exception { try { transactionManager.begin(); InvocationContext ctx = invocationContextFactory.createInvocationContext( transactionManager.getTransaction(), true); ((TxInvocationContext) ctx) .getCacheTransaction() .setStateTransferFlag(PUT_FOR_X_SITE_STATE_TRANSFER); for (XSiteState siteState : chunk) { interceptorChain.invoke(ctx, createPut(siteState)); if (trace) { log.tracef("Successfully applied key'%s'", siteState); } } transactionManager.commit(); if (debug) { log.debugf("Successfully applied state. %s keys inserted", chunk.length); } } catch (Exception e) { log.unableToApplyXSiteState(e); safeRollback(); throw e; } } private void applyStateInNonTransaction(XSiteState[] chunk) { SingleKeyNonTxInvocationContext ctx = (SingleKeyNonTxInvocationContext) invocationContextFactory.createSingleKeyNonTxInvocationContext(); for (XSiteState siteState : chunk) { PutKeyValueCommand command = createPut(siteState); ctx.setLockOwner(command.getLockOwner()); interceptorChain.invoke(ctx, command); ctx.resetState(); // re-use same context. Old context is not longer needed if (trace) { log.tracef("Successfully applied key'%s'", siteState); } } if (debug) { log.debugf("Successfully applied state. %s keys inserted", chunk.length); } } private PutKeyValueCommand createPut(XSiteState state) { return commandsFactory.buildPutKeyValueCommand( state.key(), state.value(), state.metadata(), STATE_TRANSFER_PUT_FLAGS); } private void safeRollback() { try { transactionManager.rollback(); } catch (Exception e) { // ignored! if (debug) { log.debug("Error rollbacking transaction.", e); } } } }
/** Try to figure out which TransactionManager to use */ private static void doLookups() { if (lookupFailed) return; InitialContext ctx; try { ctx = new InitialContext(); } catch (NamingException e) { log.error("Failed creating initial JNDI context", e); lookupFailed = true; return; } // probe jndi lookups first for (String[] knownJNDIManager : knownJNDIManagers) { Object jndiObject; try { if (log.isDebugEnabled()) log.debug("Trying to lookup TransactionManager for " + knownJNDIManager[1]); jndiObject = ctx.lookup(knownJNDIManager[0]); } catch (NamingException e) { log.debug( "Failed to perform a lookup for [" + knownJNDIManager[0] + " (" + knownJNDIManager[1] + ")]"); continue; } if (jndiObject instanceof TransactionManager) { tm = (TransactionManager) jndiObject; log.debug("Found TransactionManager for " + knownJNDIManager[1]); return; } } // try to find websphere lookups since we came here Class clazz; try { log.debug("Trying WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); clazz = Util.loadClassStrict(WS_FACTORY_CLASS_5_1); log.debug("Found WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); } catch (ClassNotFoundException ex) { try { log.debug("Trying WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); clazz = Util.loadClassStrict(WS_FACTORY_CLASS_5_0); log.debug("Found WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); } catch (ClassNotFoundException ex2) { try { log.debug("Trying WebSphere 4: " + WS_FACTORY_CLASS_4); clazz = Util.loadClassStrict(WS_FACTORY_CLASS_4); log.debug("Found WebSphere 4: " + WS_FACTORY_CLASS_4); } catch (ClassNotFoundException ex3) { log.debug( "Couldn't find any WebSphere TransactionManager factory class, neither for WebSphere version 5.1 nor 5.0 nor 4"); lookupFailed = true; return; } } } try { Class[] signature = null; Object[] args = null; Method method = clazz.getMethod("getTransactionManager", signature); tm = (TransactionManager) method.invoke(null, args); } catch (Exception ex) { log.error( "Found WebSphere TransactionManager factory class [" + clazz.getName() + "], but couldn't invoke its static 'getTransactionManager' method", ex); } }
@Override public void handleMarshallingException(Throwable problem, Object subject) { if (log.isDebugEnabled()) { TraceInformation.addUserInformation(problem, "toString = " + subject.toString()); } }