{"action":"created","comment":{"url":"https://api.github.com/repos/infinispan/infinispan/pulls/comments/35104805","id":35104805,"diff_hunk":"@@ -0,0 +1,373 @@\n+package org.infinispan.util.concurrent.locks.impl;\n+\n+import org.infinispan.commons.util.Notifier;\n+import org.infinispan.context.impl.TxInvocationContext;\n+import org.infinispan.factories.annotations.ComponentName;\n+import org.infinispan.factories.annotations.Inject;\n+import org.infinispan.transaction.impl.LocalTransaction;\n+import org.infinispan.transaction.impl.TransactionTable;\n+import org.infinispan.transaction.xa.CacheTransaction;\n+import org.infinispan.transaction.xa.GlobalTransaction;\n+import org.infinispan.util.TimeService;\n+import org.infinispan.util.concurrent.TimeoutException;\n+import org.infinispan.util.concurrent.locks.PendingLockListener;\n+import org.infinispan.util.concurrent.locks.PendingLockManager;\n+import org.infinispan.util.concurrent.locks.PendingLockPromise;\n+import org.infinispan.util.logging.Log;\n+import org.infinispan.util.logging.LogFactory;\n+\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.concurrent.ConcurrentHashMap;\n+import java.util.concurrent.ScheduledExecutorService;\n+import java.util.concurrent.TimeUnit;\n+import java.util.function.Predicate;\n+import java.util.stream.Collector;\n+import java.util.stream.Collectors;\n+\n+import static java.lang.String.format;\n+import static org.infinispan.commons.util.InfinispanCollections.findAnyCommon;\n+import static org.infinispan.commons.util.Util.toStr;\n+import static org.infinispan.factories.KnownComponentNames.TIMEOUT_SCHEDULE_EXECUTOR;\n+\n+/**\n+ * The default implementation for {@link PendingLockManager}.\n+ * <p>\n+ * In transactional caches, a transaction would wait for transaction originated in a older topology id. It can happen\n+ * when topology changes and a backup owner becomes the primary owner.\n+ *\n+ * @author Pedro Ruivo\n+ * @since 8.0\n+ */\n+public class DefaultPendingLockManager implements PendingLockManager {\n+\n+   private static final Log log = LogFactory.getLog(DefaultPendingLockManager.class);\n+   private static final boolean trace = log.isTraceEnabled();\n+   private static final int NO_PENDING_CHECK = -2;\n+   private final Map<GlobalTransaction, PendingLockPromiseImpl> pendingLockPromiseMap;\n+   private TransactionTable transactionTable;\n+   private TimeService timeService;\n+   private ScheduledExecutorService timeoutExecutor;\n+\n+   public DefaultPendingLockManager() {\n+      pendingLockPromiseMap = new ConcurrentHashMap<>();\n+   }\n+\n+   @Inject\n+   public void inject(TransactionTable transactionTable, TimeService timeService,\n+                      @ComponentName(TIMEOUT_SCHEDULE_EXECUTOR) ScheduledExecutorService timeoutExecutor) {\n+      this.transactionTable = transactionTable;\n+      this.timeService = timeService;\n+      this.timeoutExecutor = timeoutExecutor;\n+   }\n+\n+   @Override\n+   public PendingLockPromise checkPendingForKey(TxInvocationContext<?> ctx, Object key, long time, TimeUnit unit) {\n+      final GlobalTransaction globalTransaction = ctx.getGlobalTransaction();\n+      PendingLockPromiseImpl existing = pendingLockPromiseMap.get(globalTransaction);\n+      if (existing != null) {\n+         return existing;\n+      }\n+      final int txTopologyId = getTopologyId(ctx);\n+      if (txTopologyId != NO_PENDING_CHECK) {\n+         if (trace) {\n+            log.tracef(\"Checking for pending locks and then locking key %s\", toStr(key));\n+         }\n+         return createAndStore(getTransactionWithLockedKey(txTopologyId, key, globalTransaction),\n+                               globalTransaction, time, unit);\n+      }\n+      return PendingLockPromise.NO_OP;\n+   }\n+\n+   @Override\n+   public PendingLockPromise checkPendingForAllKeys(TxInvocationContext<?> ctx, Collection<Object> keys,\n+                                                    long time, TimeUnit unit) {\n+      final GlobalTransaction globalTransaction = ctx.getGlobalTransaction();\n+      PendingLockPromiseImpl existing = pendingLockPromiseMap.get(globalTransaction);\n+      if (existing != null) {\n+         return existing;\n+      }\n+      final int txTopologyId = getTopologyId(ctx);\n+      if (txTopologyId != NO_PENDING_CHECK) {\n+         if (trace) {\n+            log.tracef(\"Checking for pending locks and then locking keys %s\", toStr(keys));\n+         }\n+         return createAndStore(getTransactionWithAnyLockedKey(txTopologyId, keys, globalTransaction),\n+                               globalTransaction, time, unit);\n+      }\n+      return PendingLockPromise.NO_OP;\n+   }\n+\n+   @Override\n+   public long awaitPendingTransactionsForKey(TxInvocationContext<?> ctx, Object key,\n+                                              long time, TimeUnit unit) throws InterruptedException {\n+      PendingLockPromiseImpl pendingLockPromise = pendingLockPromiseMap.remove(ctx.getGlobalTransaction());\n+      if (pendingLockPromise != null) {\n+         pendingLockPromise.await();\n+         if (pendingLockPromise.hasTimedOut()) {\n+            timeoutForKey(key, pendingLockPromise.getTimedOutTransaction(), ctx.getGlobalTransaction());\n+         }\n+         return pendingLockPromise.getRemainingTimeout();\n+      }\n+      final int txTopologyId = getTopologyId(ctx);\n+      if (txTopologyId != NO_PENDING_CHECK) {\n+         return checkForPendingLock(key, ctx.getGlobalTransaction(), txTopologyId, unit.toMillis(time));\n+      }\n+\n+      if (trace) {\n+         log.tracef(\"Locking key %s, no need to check for pending locks.\", toStr(key));\n+      }\n+      return unit.toMillis(time);\n+   }\n+\n+   @Override\n+   public long awaitPendingTransactionsForAllKeys(TxInvocationContext<?> ctx, Collection<Object> keys,\n+                                                  long time, TimeUnit unit) throws InterruptedException {\n+      PendingLockPromiseImpl pendingLockPromise = pendingLockPromiseMap.remove(ctx.getGlobalTransaction());\n+      if (pendingLockPromise != null) {\n+         pendingLockPromise.await();\n+         if (pendingLockPromise.hasTimedOut()) {\n+            timeoutForAnyKey(keys, pendingLockPromise.getTimedOutTransaction(), ctx.getGlobalTransaction());\n+         }\n+         return pendingLockPromise.getRemainingTimeout();\n+      }\n+      final int txTopologyId = getTopologyId(ctx);\n+      if (txTopologyId != NO_PENDING_CHECK) {\n+         return checkForPendingAnyLocks(keys, ctx.getGlobalTransaction(), txTopologyId, unit.toMillis(time));\n+      }\n+\n+      if (trace) {\n+         log.tracef(\"Locking keys %s, no need to check for pending locks.\", toStr(keys));\n+      }\n+\n+      return unit.toMillis(time);\n+   }\n+\n+   private PendingLockPromise createAndStore(Collection<CacheTransaction> transactions,\n+                                             GlobalTransaction globalTransaction, long time, TimeUnit unit) {\n+      if (transactions.isEmpty()) {\n+         return PendingLockPromise.NO_OP;\n+      }\n+\n+      PendingLockPromiseImpl pendingLockPromise = new PendingLockPromiseImpl(transactions, timeService.expectedEndTime(time, unit));\n+      PendingLockPromiseImpl existing = pendingLockPromiseMap.putIfAbsent(globalTransaction, pendingLockPromise);\n+      if (existing != null) {\n+         return existing;\n+      }\n+      pendingLockPromise.registerListenerInCacheTransactions();\n+      if (!pendingLockPromise.isReady()) {\n+         timeoutExecutor.schedule(pendingLockPromise, time, unit);\n+      }\n+      return pendingLockPromise;\n+   }\n+\n+   private int getTopologyId(TxInvocationContext<?> context) {\n+      final CacheTransaction tx = context.getCacheTransaction();\n+      boolean isFromStateTransfer = context.isOriginLocal() && ((LocalTransaction) tx).isFromStateTransfer();\n+      // if the transaction is from state transfer it should not wait for the backup locks of other transactions\n+      if (!isFromStateTransfer) {\n+         final int transactionTopologyId = tx.getTopologyId();\n+         if (transactionTopologyId != TransactionTable.CACHE_STOPPED_TOPOLOGY_ID) {\n+            if (transactionTable.getMinTopologyId() < transactionTopologyId) {\n+               return transactionTopologyId;\n+            }\n+         }\n+      }\n+      return NO_PENDING_CHECK;\n+   }\n+\n+   private long checkForPendingLock(Object key, GlobalTransaction globalTransaction, int transactionTopologyId, long lockTimeout) throws InterruptedException {\n+      if (trace) {\n+         log.tracef(\"Checking for pending locks and then locking key %s\", toStr(key));\n+      }\n+\n+      final long expectedEndTime = timeService.expectedEndTime(lockTimeout, TimeUnit.MILLISECONDS);\n+\n+      final CacheTransaction lockOwner = waitForTransactionsToComplete(getTransactionWithLockedKey(transactionTopologyId, key, globalTransaction),\n+                                                                       expectedEndTime);\n+\n+      // Then try to acquire a lock\n+      if (trace) {\n+         log.tracef(\"Finished waiting for other potential lockers, trying to acquire the lock on %s\", toStr(key));\n+      }\n+\n+      if (lockOwner != null) {\n+         timeoutForKey(key, lockOwner, globalTransaction);\n+      }\n+\n+      return timeService.remainingTime(expectedEndTime, TimeUnit.MILLISECONDS);\n+   }\n+\n+   private long checkForPendingAnyLocks(Collection<Object> keys, GlobalTransaction globalTransaction, int transactionTopologyId, long lockTimeout) throws InterruptedException {\n+      if (trace)\n+         log.tracef(\"Checking for pending locks and then locking key %s\", toStr(keys));\n+\n+      final long expectedEndTime = timeService.expectedEndTime(lockTimeout, TimeUnit.MILLISECONDS);\n+\n+      final CacheTransaction lockOwner = waitForTransactionsToComplete(getTransactionWithAnyLockedKey(transactionTopologyId, keys, globalTransaction),","path":"core/src/main/java/org/infinispan/util/concurrent/locks/impl/DefaultPendingLockManager.java","position":210,"original_position":210,"commit_id":"546ba847209a412bcaf8aecee7e96eef1e5a7dbf","original_commit_id":"546ba847209a412bcaf8aecee7e96eef1e5a7dbf","user":{"login":"wburns","id":648175,"avatar_url":"https://avatars.githubusercontent.com/u/648175?v=3","gravatar_id":"","url":"https://api.github.com/users/wburns","html_url":"https://github.com/wburns","followers_url":"https://api.github.com/users/wburns/followers","following_url":"https://api.github.com/users/wburns/following{/other_user}","gists_url":"https://api.github.com/users/wburns/gists{/gist_id}","starred_url":"https://api.github.com/users/wburns/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wburns/subscriptions","organizations_url":"https://api.github.com/users/wburns/orgs","repos_url":"https://api.github.com/users/wburns/repos","events_url":"https://api.github.com/users/wburns/events{/privacy}","received_events_url":"https://api.github.com/users/wburns/received_events","type":"User","site_admin":false},"body":"Thanks!  Makes sense, I am not as familiar with the locking deep down nitty gritty details.","created_at":"2015-07-21T14:03:49Z","updated_at":"2015-07-21T14:03:49Z","html_url":"https://github.com/infinispan/infinispan/pull/3594#discussion_r35104805","pull_request_url":"https://api.github.com/repos/infinispan/infinispan/pulls/3594","_links":{"self":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/comments/35104805"},"html":{"href":"https://github.com/infinispan/infinispan/pull/3594#discussion_r35104805"},"pull_request":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/3594"}}},"pull_request":{"url":"https://api.github.com/repos/infinispan/infinispan/pulls/3594","id":40349523,"html_url":"https://github.com/infinispan/infinispan/pull/3594","diff_url":"https://github.com/infinispan/infinispan/pull/3594.diff","patch_url":"https://github.com/infinispan/infinispan/pull/3594.patch","issue_url":"https://api.github.com/repos/infinispan/infinispan/issues/3594","number":3594,"state":"open","locked":false,"title":"ISPN-5620 Create PendingLockManager","user":{"login":"pruivo","id":1492066,"avatar_url":"https://avatars.githubusercontent.com/u/1492066?v=3","gravatar_id":"","url":"https://api.github.com/users/pruivo","html_url":"https://github.com/pruivo","followers_url":"https://api.github.com/users/pruivo/followers","following_url":"https://api.github.com/users/pruivo/following{/other_user}","gists_url":"https://api.github.com/users/pruivo/gists{/gist_id}","starred_url":"https://api.github.com/users/pruivo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pruivo/subscriptions","organizations_url":"https://api.github.com/users/pruivo/orgs","repos_url":"https://api.github.com/users/pruivo/repos","events_url":"https://api.github.com/users/pruivo/events{/privacy}","received_events_url":"https://api.github.com/users/pruivo/received_events","type":"User","site_admin":false},"body":"https://issues.jboss.org/browse/ISPN-5620\r\n\r\nPendingLockManager will be responsible to check for older topology id transaction that may have locks acquired. Newer transactions would have to wait for them to complete before proceeding in lock acquisition.\r\n","created_at":"2015-07-20T13:06:20Z","updated_at":"2015-07-21T14:03:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2b9a7a157e43f29f87a5a2b1374e97ff643ca7f3","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/infinispan/infinispan/pulls/3594/commits","review_comments_url":"https://api.github.com/repos/infinispan/infinispan/pulls/3594/comments","review_comment_url":"https://api.github.com/repos/infinispan/infinispan/pulls/comments{/number}","comments_url":"https://api.github.com/repos/infinispan/infinispan/issues/3594/comments","statuses_url":"https://api.github.com/repos/infinispan/infinispan/statuses/546ba847209a412bcaf8aecee7e96eef1e5a7dbf","head":{"label":"pruivo:pending_lock_manager","ref":"pending_lock_manager","sha":"546ba847209a412bcaf8aecee7e96eef1e5a7dbf","user":{"login":"pruivo","id":1492066,"avatar_url":"https://avatars.githubusercontent.com/u/1492066?v=3","gravatar_id":"","url":"https://api.github.com/users/pruivo","html_url":"https://github.com/pruivo","followers_url":"https://api.github.com/users/pruivo/followers","following_url":"https://api.github.com/users/pruivo/following{/other_user}","gists_url":"https://api.github.com/users/pruivo/gists{/gist_id}","starred_url":"https://api.github.com/users/pruivo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pruivo/subscriptions","organizations_url":"https://api.github.com/users/pruivo/orgs","repos_url":"https://api.github.com/users/pruivo/repos","events_url":"https://api.github.com/users/pruivo/events{/privacy}","received_events_url":"https://api.github.com/users/pruivo/received_events","type":"User","site_admin":false},"repo":{"id":3626075,"name":"infinispan","full_name":"pruivo/infinispan","owner":{"login":"pruivo","id":1492066,"avatar_url":"https://avatars.githubusercontent.com/u/1492066?v=3","gravatar_id":"","url":"https://api.github.com/users/pruivo","html_url":"https://github.com/pruivo","followers_url":"https://api.github.com/users/pruivo/followers","following_url":"https://api.github.com/users/pruivo/following{/other_user}","gists_url":"https://api.github.com/users/pruivo/gists{/gist_id}","starred_url":"https://api.github.com/users/pruivo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pruivo/subscriptions","organizations_url":"https://api.github.com/users/pruivo/orgs","repos_url":"https://api.github.com/users/pruivo/repos","events_url":"https://api.github.com/users/pruivo/events{/privacy}","received_events_url":"https://api.github.com/users/pruivo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pruivo/infinispan","description":"Infinispan is an open source data grid platform and highly scalable NoSQL cloud data store.","fork":true,"url":"https://api.github.com/repos/pruivo/infinispan","forks_url":"https://api.github.com/repos/pruivo/infinispan/forks","keys_url":"https://api.github.com/repos/pruivo/infinispan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pruivo/infinispan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pruivo/infinispan/teams","hooks_url":"https://api.github.com/repos/pruivo/infinispan/hooks","issue_events_url":"https://api.github.com/repos/pruivo/infinispan/issues/events{/number}","events_url":"https://api.github.com/repos/pruivo/infinispan/events","assignees_url":"https://api.github.com/repos/pruivo/infinispan/assignees{/user}","branches_url":"https://api.github.com/repos/pruivo/infinispan/branches{/branch}","tags_url":"https://api.github.com/repos/pruivo/infinispan/tags","blobs_url":"https://api.github.com/repos/pruivo/infinispan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pruivo/infinispan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pruivo/infinispan/git/refs{/sha}","trees_url":"https://api.github.com/repos/pruivo/infinispan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pruivo/infinispan/statuses/{sha}","languages_url":"https://api.github.com/repos/pruivo/infinispan/languages","stargazers_url":"https://api.github.com/repos/pruivo/infinispan/stargazers","contributors_url":"https://api.github.com/repos/pruivo/infinispan/contributors","subscribers_url":"https://api.github.com/repos/pruivo/infinispan/subscribers","subscription_url":"https://api.github.com/repos/pruivo/infinispan/subscription","commits_url":"https://api.github.com/repos/pruivo/infinispan/commits{/sha}","git_commits_url":"https://api.github.com/repos/pruivo/infinispan/git/commits{/sha}","comments_url":"https://api.github.com/repos/pruivo/infinispan/comments{/number}","issue_comment_url":"https://api.github.com/repos/pruivo/infinispan/issues/comments{/number}","contents_url":"https://api.github.com/repos/pruivo/infinispan/contents/{+path}","compare_url":"https://api.github.com/repos/pruivo/infinispan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pruivo/infinispan/merges","archive_url":"https://api.github.com/repos/pruivo/infinispan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pruivo/infinispan/downloads","issues_url":"https://api.github.com/repos/pruivo/infinispan/issues{/number}","pulls_url":"https://api.github.com/repos/pruivo/infinispan/pulls{/number}","milestones_url":"https://api.github.com/repos/pruivo/infinispan/milestones{/number}","notifications_url":"https://api.github.com/repos/pruivo/infinispan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pruivo/infinispan/labels{/name}","releases_url":"https://api.github.com/repos/pruivo/infinispan/releases{/id}","created_at":"2012-03-05T10:40:21Z","updated_at":"2015-05-08T17:30:13Z","pushed_at":"2015-07-21T10:52:39Z","git_url":"git://github.com/pruivo/infinispan.git","ssh_url":"git@github.com:pruivo/infinispan.git","clone_url":"https://github.com/pruivo/infinispan.git","svn_url":"https://github.com/pruivo/infinispan","homepage":"http://www.infinispan.org","size":77125,"stargazers_count":3,"watchers_count":3,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":0,"forks":6,"open_issues":0,"watchers":3,"default_branch":"master"}},"base":{"label":"infinispan:master","ref":"master","sha":"78ab764539dabc098cdd2fd78f641e81db1fbc55","user":{"login":"infinispan","id":458093,"avatar_url":"https://avatars.githubusercontent.com/u/458093?v=3","gravatar_id":"","url":"https://api.github.com/users/infinispan","html_url":"https://github.com/infinispan","followers_url":"https://api.github.com/users/infinispan/followers","following_url":"https://api.github.com/users/infinispan/following{/other_user}","gists_url":"https://api.github.com/users/infinispan/gists{/gist_id}","starred_url":"https://api.github.com/users/infinispan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/infinispan/subscriptions","organizations_url":"https://api.github.com/users/infinispan/orgs","repos_url":"https://api.github.com/users/infinispan/repos","events_url":"https://api.github.com/users/infinispan/events{/privacy}","received_events_url":"https://api.github.com/users/infinispan/received_events","type":"Organization","site_admin":false},"repo":{"id":1050944,"name":"infinispan","full_name":"infinispan/infinispan","owner":{"login":"infinispan","id":458093,"avatar_url":"https://avatars.githubusercontent.com/u/458093?v=3","gravatar_id":"","url":"https://api.github.com/users/infinispan","html_url":"https://github.com/infinispan","followers_url":"https://api.github.com/users/infinispan/followers","following_url":"https://api.github.com/users/infinispan/following{/other_user}","gists_url":"https://api.github.com/users/infinispan/gists{/gist_id}","starred_url":"https://api.github.com/users/infinispan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/infinispan/subscriptions","organizations_url":"https://api.github.com/users/infinispan/orgs","repos_url":"https://api.github.com/users/infinispan/repos","events_url":"https://api.github.com/users/infinispan/events{/privacy}","received_events_url":"https://api.github.com/users/infinispan/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/infinispan/infinispan","description":"Infinispan is an open source data grid platform and highly scalable NoSQL cloud data store.","fork":false,"url":"https://api.github.com/repos/infinispan/infinispan","forks_url":"https://api.github.com/repos/infinispan/infinispan/forks","keys_url":"https://api.github.com/repos/infinispan/infinispan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/infinispan/infinispan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/infinispan/infinispan/teams","hooks_url":"https://api.github.com/repos/infinispan/infinispan/hooks","issue_events_url":"https://api.github.com/repos/infinispan/infinispan/issues/events{/number}","events_url":"https://api.github.com/repos/infinispan/infinispan/events","assignees_url":"https://api.github.com/repos/infinispan/infinispan/assignees{/user}","branches_url":"https://api.github.com/repos/infinispan/infinispan/branches{/branch}","tags_url":"https://api.github.com/repos/infinispan/infinispan/tags","blobs_url":"https://api.github.com/repos/infinispan/infinispan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/infinispan/infinispan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/infinispan/infinispan/git/refs{/sha}","trees_url":"https://api.github.com/repos/infinispan/infinispan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/infinispan/infinispan/statuses/{sha}","languages_url":"https://api.github.com/repos/infinispan/infinispan/languages","stargazers_url":"https://api.github.com/repos/infinispan/infinispan/stargazers","contributors_url":"https://api.github.com/repos/infinispan/infinispan/contributors","subscribers_url":"https://api.github.com/repos/infinispan/infinispan/subscribers","subscription_url":"https://api.github.com/repos/infinispan/infinispan/subscription","commits_url":"https://api.github.com/repos/infinispan/infinispan/commits{/sha}","git_commits_url":"https://api.github.com/repos/infinispan/infinispan/git/commits{/sha}","comments_url":"https://api.github.com/repos/infinispan/infinispan/comments{/number}","issue_comment_url":"https://api.github.com/repos/infinispan/infinispan/issues/comments{/number}","contents_url":"https://api.github.com/repos/infinispan/infinispan/contents/{+path}","compare_url":"https://api.github.com/repos/infinispan/infinispan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/infinispan/infinispan/merges","archive_url":"https://api.github.com/repos/infinispan/infinispan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/infinispan/infinispan/downloads","issues_url":"https://api.github.com/repos/infinispan/infinispan/issues{/number}","pulls_url":"https://api.github.com/repos/infinispan/infinispan/pulls{/number}","milestones_url":"https://api.github.com/repos/infinispan/infinispan/milestones{/number}","notifications_url":"https://api.github.com/repos/infinispan/infinispan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/infinispan/infinispan/labels{/name}","releases_url":"https://api.github.com/repos/infinispan/infinispan/releases{/id}","created_at":"2010-11-04T12:33:19Z","updated_at":"2015-07-20T20:05:17Z","pushed_at":"2015-07-21T13:19:02Z","git_url":"git://github.com/infinispan/infinispan.git","ssh_url":"git@github.com:infinispan/infinispan.git","clone_url":"https://github.com/infinispan/infinispan.git","svn_url":"https://github.com/infinispan/infinispan","homepage":"http://www.infinispan.org","size":155202,"stargazers_count":389,"watchers_count":389,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":296,"mirror_url":null,"open_issues_count":22,"forks":296,"open_issues":22,"watchers":389,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/3594"},"html":{"href":"https://github.com/infinispan/infinispan/pull/3594"},"issue":{"href":"https://api.github.com/repos/infinispan/infinispan/issues/3594"},"comments":{"href":"https://api.github.com/repos/infinispan/infinispan/issues/3594/comments"},"review_comments":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/3594/comments"},"review_comment":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/infinispan/infinispan/pulls/3594/commits"},"statuses":{"href":"https://api.github.com/repos/infinispan/infinispan/statuses/546ba847209a412bcaf8aecee7e96eef1e5a7dbf"}}}}