{"action":"created","comment":{"url":"https://api.github.com/repos/Jasig/cas/pulls/comments/46500630","id":46500630,"diff_hunk":"@@ -0,0 +1,317 @@\n+package org.jasig.cas.ticket.registry;\n+\n+import org.jasig.cas.authentication.principal.Service;\n+import org.jasig.cas.ticket.registry.encrypt.AbstractCrypticTicketRegistry;\n+import org.jasig.cas.ticket.ServiceTicket;\n+import org.jasig.cas.ticket.Ticket;\n+import org.jasig.cas.ticket.TicketGrantingTicket;\n+\n+import org.apache.commons.lang3.BooleanUtils;\n+import org.apache.commons.lang3.StringUtils;\n+import org.apache.ignite.cache.CachePeekMode;\n+import org.apache.ignite.cache.query.QueryCursor;\n+import org.apache.ignite.cache.query.ScanQuery;\n+import org.apache.ignite.configuration.CacheConfiguration;\n+import org.apache.ignite.configuration.IgniteConfiguration;\n+import org.apache.ignite.Ignite;\n+import org.apache.ignite.IgniteCache;\n+import org.apache.ignite.IgniteState;\n+import org.apache.ignite.Ignition;\n+import org.apache.ignite.lang.IgniteBiPredicate;\n+import org.springframework.beans.factory.annotation.Autowired;\n+import org.springframework.beans.factory.annotation.Qualifier;\n+import org.springframework.beans.factory.annotation.Value;\n+import org.springframework.beans.factory.DisposableBean;\n+import org.springframework.core.style.ToStringCreator;\n+import org.springframework.stereotype.Component;\n+\n+import java.util.Collection;\n+import java.util.concurrent.TimeUnit;\n+import java.util.HashSet;\n+import java.util.Map;\n+import javax.annotation.PostConstruct;\n+import javax.cache.Cache.Entry;\n+import javax.cache.Cache;\n+import javax.cache.expiry.CreatedExpiryPolicy;\n+import javax.cache.expiry.Duration;\n+import javax.validation.constraints.NotNull;\n+\n+\n+/**\n+ * <p>\n+ * <a href=\"https://ignite.apache.org\">Ignite</a> based distributed ticket registry.\n+ * </p>\n+ *\n+ * <p>\n+ * Use distinct caches for ticket granting tickets (TGT) and service tickets (ST) for:\n+ * <ul>\n+ *   <li>Tuning : use cache level time to live with different values for TGT an ST.</li>\n+ *   <li>Monitoring : follow separately the number of TGT and ST.</li>\n+ * </ul>\n+ *\n+ * @author tduehr\n+ * @since 4.3.0`\n+ */\n+@Component(\"igniteTicketRegistry\")\n+public final class IgniteTicketRegistry extends AbstractCrypticTicketRegistry implements DisposableBean {\n+\n+    @Autowired\n+    @NotNull\n+    @Value(\"${ignite.ticketsCache.name:serviceTicketsCache}\")\n+    private String servicesCacheName;\n+\n+    @NotNull\n+    @Value(\"${ignite.ticketsCache.name:ticketGrantingTicketsCache}\")\n+    private String ticketsCacheName;\n+\n+    @Autowired\n+    @NotNull\n+    @Qualifier(\"igniteConfiguration\")\n+    private IgniteConfiguration igniteConfiguration;\n+\n+    private IgniteCache<String, ServiceTicket> serviceTicketsCache;\n+    private IgniteCache<String, TicketGrantingTicket> ticketGrantingTicketsCache;\n+\n+    @Value(\"${tgt.maxTimeToLiveInSeconds:28800}\")\n+    private long ticketGrantingTicketTimeoutInSeconds = 28800;\n+\n+    @Value(\"${st.timeToKillInSeconds:10}\")\n+    private long serviceTicketTimeoutInSeconds = 10;\n+\n+    private Ignite ignite;\n+\n+    /**\n+     * @see #setSupportRegistryState(boolean)\n+     **/\n+    private boolean supportRegistryState = true;\n+\n+    /**\n+     * Instantiates a new Ignite ticket registry.\n+     */\n+    public IgniteTicketRegistry() {\n+        super();\n+    }\n+\n+    @Override\n+    public void addTicket(final Ticket ticketToAdd) {\n+        final Ticket ticket = encodeTicket(ticketToAdd);\n+        if (ticket instanceof ServiceTicket) {\n+            logger.debug(\"Adding service ticket {} to the cache {}\", ticket.getId(), this.serviceTicketsCache.getName());\n+            this.serviceTicketsCache.put(ticket.getId(), (ServiceTicket) ticket);\n+        } else if (ticket instanceof TicketGrantingTicket) {\n+            logger.debug(\"Adding ticket granting ticket {} to the cache {}\", ticket.getId(), this.ticketGrantingTicketsCache.getName());\n+            this.ticketGrantingTicketsCache.put(ticket.getId(), (TicketGrantingTicket) ticket);\n+        } else {\n+            throw new IllegalArgumentException(\"Invalid ticket type \" + ticket);\n+        }\n+    }\n+\n+    @Override\n+    public boolean deleteTicket(final String ticketIdToDelete) {\n+        final String ticketId = encodeTicketId(ticketIdToDelete);\n+        if (StringUtils.isBlank(ticketId)) {\n+            return false;\n+        }\n+\n+        final Ticket ticket = getTicket(ticketId);\n+        if (ticket == null) {\n+            return false;\n+        }\n+\n+        if (ticket instanceof TicketGrantingTicket) {\n+            logger.debug(\"Removing ticket [{}] and its children from the registry.\", ticket);\n+            return deleteTicketAndChildren((TicketGrantingTicket) ticket);\n+        }\n+\n+        logger.debug(\"Removing ticket [{}] from the registry.\", ticket);\n+        return this.serviceTicketsCache.remove(ticketId);\n+    }\n+\n+    /**\n+     * Delete the TGT and all of its service tickets.\n+     *\n+     * @param ticket the ticket\n+     * @return boolean indicating whether ticket was deleted or not\n+     */\n+    private boolean deleteTicketAndChildren(final TicketGrantingTicket ticket) {\n+        final Map<String, Service> services = ticket.getServices();\n+        if (services != null && !services.isEmpty()) {\n+            this.serviceTicketsCache.removeAll(services.keySet());\n+        }\n+\n+        return this.ticketGrantingTicketsCache.remove(ticket.getId());\n+    }\n+\n+    @Override\n+    public Ticket getTicket(final String ticketIdToGet) {\n+        final String ticketId = encodeTicketId(ticketIdToGet);\n+        if (ticketId == null) {\n+            return null;\n+        }\n+\n+        Ticket ticket = this.serviceTicketsCache.get(ticketId);\n+        if (ticket == null) {\n+            ticket = this.ticketGrantingTicketsCache.get(ticketId);\n+        }\n+        if (ticket == null) {\n+            logger.debug(\"No ticket by id [{}] is found in the registry\", ticketId);\n+            return null;\n+        }\n+\n+        final Ticket proxiedTicket = decodeTicket(ticket);\n+        ticket = getProxiedTicketInstance(proxiedTicket);\n+        return ticket;\n+    }\n+\n+    @Override\n+    public Collection<Ticket> getTickets() {\n+        final Collection<Cache.Entry<String, Ticket>> serviceTickets;\n+        final Collection<Cache.Entry<String, Ticket>> tgtTicketsTickets;\n+\n+        final IgniteBiPredicate<String, Ticket> filter = new IgniteBiPredicate<String, Ticket>() {\n+          @Override\n+          public boolean apply(final String key, final Ticket t) {\n+            return !t.isExpired();\n+          }\n+        };\n+\n+        QueryCursor<Cache.Entry<String, Ticket>> cursor = ticketGrantingTicketsCache.query(new ScanQuery<>(filter));\n+        tgtTicketsTickets = cursor.getAll();\n+\n+        cursor = serviceTicketsCache.query(new ScanQuery<>(filter));\n+        serviceTickets = cursor.getAll();\n+\n+        final Collection<Ticket> allTickets = new HashSet<Ticket>(serviceTickets.size() + tgtTicketsTickets.size());\n+\n+        for (final Cache.Entry<String, Ticket> entry : serviceTickets) {\n+            final Ticket proxiedTicket = getProxiedTicketInstance(entry.getValue());\n+            allTickets.add(proxiedTicket);\n+        }\n+\n+        for (final Cache.Entry<String, Ticket> entry : tgtTicketsTickets) {\n+            final Ticket proxiedTicket = getProxiedTicketInstance(entry.getValue());\n+            allTickets.add(proxiedTicket);\n+        }\n+\n+        return decodeTickets(allTickets);\n+    }\n+\n+    public void setServiceTicketsCache(final IgniteCache<String, ServiceTicket> serviceTicketsCache) {\n+        this.serviceTicketsCache = serviceTicketsCache;\n+    }\n+\n+    public void setTicketGrantingTicketsCache(final IgniteCache<String, TicketGrantingTicket> ticketGrantingTicketsCache) {\n+        this.ticketGrantingTicketsCache = ticketGrantingTicketsCache;\n+    }\n+\n+    public void setIgniteConfiguration(final IgniteConfiguration igniteConfiguration){\n+        this.igniteConfiguration = igniteConfiguration;\n+    }\n+\n+    public IgniteConfiguration getIgniteConfiguration(){\n+        return this.igniteConfiguration;\n+    }\n+\n+    @Override\n+    public String toString() {\n+        return new ToStringCreator(this).append(\"ticketGrantingTicketsCache\", this.ticketGrantingTicketsCache)","path":"cas-server-integration-ignite/src/main/java/org/jasig/cas/ticket/registry/IgniteTicketRegistry.java","position":217,"original_position":217,"commit_id":"e63ad7952be30de21b03f819a6fa26ec226dc776","original_commit_id":"e63ad7952be30de21b03f819a6fa26ec226dc776","user":{"login":"tduehr","id":37545,"avatar_url":"https://avatars.githubusercontent.com/u/37545?v=3","gravatar_id":"","url":"https://api.github.com/users/tduehr","html_url":"https://github.com/tduehr","followers_url":"https://api.github.com/users/tduehr/followers","following_url":"https://api.github.com/users/tduehr/following{/other_user}","gists_url":"https://api.github.com/users/tduehr/gists{/gist_id}","starred_url":"https://api.github.com/users/tduehr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tduehr/subscriptions","organizations_url":"https://api.github.com/users/tduehr/orgs","repos_url":"https://api.github.com/users/tduehr/repos","events_url":"https://api.github.com/users/tduehr/events{/privacy}","received_events_url":"https://api.github.com/users/tduehr/received_events","type":"User","site_admin":false},"body":"Also a holdover from EhCache.","created_at":"2015-12-03T01:00:37Z","updated_at":"2015-12-03T01:00:37Z","html_url":"https://github.com/Jasig/cas/pull/1293#discussion_r46500630","pull_request_url":"https://api.github.com/repos/Jasig/cas/pulls/1293","_links":{"self":{"href":"https://api.github.com/repos/Jasig/cas/pulls/comments/46500630"},"html":{"href":"https://github.com/Jasig/cas/pull/1293#discussion_r46500630"},"pull_request":{"href":"https://api.github.com/repos/Jasig/cas/pulls/1293"}}},"pull_request":{"url":"https://api.github.com/repos/Jasig/cas/pulls/1293","id":51408655,"html_url":"https://github.com/Jasig/cas/pull/1293","diff_url":"https://github.com/Jasig/cas/pull/1293.diff","patch_url":"https://github.com/Jasig/cas/pull/1293.patch","issue_url":"https://api.github.com/repos/Jasig/cas/issues/1293","number":1293,"state":"open","locked":false,"title":"Add Apache Ignite ticket cache support","user":{"login":"tduehr","id":37545,"avatar_url":"https://avatars.githubusercontent.com/u/37545?v=3","gravatar_id":"","url":"https://api.github.com/users/tduehr","html_url":"https://github.com/tduehr","followers_url":"https://api.github.com/users/tduehr/followers","following_url":"https://api.github.com/users/tduehr/following{/other_user}","gists_url":"https://api.github.com/users/tduehr/gists{/gist_id}","starred_url":"https://api.github.com/users/tduehr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tduehr/subscriptions","organizations_url":"https://api.github.com/users/tduehr/orgs","repos_url":"https://api.github.com/users/tduehr/repos","events_url":"https://api.github.com/users/tduehr/events{/privacy}","received_events_url":"https://api.github.com/users/tduehr/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-11-20T21:10:10Z","updated_at":"2015-12-03T01:00:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ecb5261075f8f4c3cebd234439a342e506cf70f3","assignee":null,"milestone":{"url":"https://api.github.com/repos/Jasig/cas/milestones/15","html_url":"https://github.com/Jasig/cas/milestones/4.3.0","labels_url":"https://api.github.com/repos/Jasig/cas/milestones/15/labels","id":1349862,"number":15,"title":"4.3.0","description":"CAS version 4.3.0 minor release","creator":{"login":"mmoayyed","id":1205228,"avatar_url":"https://avatars.githubusercontent.com/u/1205228?v=3","gravatar_id":"","url":"https://api.github.com/users/mmoayyed","html_url":"https://github.com/mmoayyed","followers_url":"https://api.github.com/users/mmoayyed/followers","following_url":"https://api.github.com/users/mmoayyed/following{/other_user}","gists_url":"https://api.github.com/users/mmoayyed/gists{/gist_id}","starred_url":"https://api.github.com/users/mmoayyed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mmoayyed/subscriptions","organizations_url":"https://api.github.com/users/mmoayyed/orgs","repos_url":"https://api.github.com/users/mmoayyed/repos","events_url":"https://api.github.com/users/mmoayyed/events{/privacy}","received_events_url":"https://api.github.com/users/mmoayyed/received_events","type":"User","site_admin":false},"open_issues":13,"closed_issues":2,"state":"open","created_at":"2015-10-12T01:28:38Z","updated_at":"2015-11-30T15:11:10Z","due_on":"2016-04-19T07:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/Jasig/cas/pulls/1293/commits","review_comments_url":"https://api.github.com/repos/Jasig/cas/pulls/1293/comments","review_comment_url":"https://api.github.com/repos/Jasig/cas/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Jasig/cas/issues/1293/comments","statuses_url":"https://api.github.com/repos/Jasig/cas/statuses/e63ad7952be30de21b03f819a6fa26ec226dc776","head":{"label":"tduehr:ignite","ref":"ignite","sha":"e63ad7952be30de21b03f819a6fa26ec226dc776","user":{"login":"tduehr","id":37545,"avatar_url":"https://avatars.githubusercontent.com/u/37545?v=3","gravatar_id":"","url":"https://api.github.com/users/tduehr","html_url":"https://github.com/tduehr","followers_url":"https://api.github.com/users/tduehr/followers","following_url":"https://api.github.com/users/tduehr/following{/other_user}","gists_url":"https://api.github.com/users/tduehr/gists{/gist_id}","starred_url":"https://api.github.com/users/tduehr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tduehr/subscriptions","organizations_url":"https://api.github.com/users/tduehr/orgs","repos_url":"https://api.github.com/users/tduehr/repos","events_url":"https://api.github.com/users/tduehr/events{/privacy}","received_events_url":"https://api.github.com/users/tduehr/received_events","type":"User","site_admin":false},"repo":{"id":41937159,"name":"cas","full_name":"tduehr/cas","owner":{"login":"tduehr","id":37545,"avatar_url":"https://avatars.githubusercontent.com/u/37545?v=3","gravatar_id":"","url":"https://api.github.com/users/tduehr","html_url":"https://github.com/tduehr","followers_url":"https://api.github.com/users/tduehr/followers","following_url":"https://api.github.com/users/tduehr/following{/other_user}","gists_url":"https://api.github.com/users/tduehr/gists{/gist_id}","starred_url":"https://api.github.com/users/tduehr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tduehr/subscriptions","organizations_url":"https://api.github.com/users/tduehr/orgs","repos_url":"https://api.github.com/users/tduehr/repos","events_url":"https://api.github.com/users/tduehr/events{/privacy}","received_events_url":"https://api.github.com/users/tduehr/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tduehr/cas","description":"Apereo CAS - Single Sign On for the Web","fork":true,"url":"https://api.github.com/repos/tduehr/cas","forks_url":"https://api.github.com/repos/tduehr/cas/forks","keys_url":"https://api.github.com/repos/tduehr/cas/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tduehr/cas/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tduehr/cas/teams","hooks_url":"https://api.github.com/repos/tduehr/cas/hooks","issue_events_url":"https://api.github.com/repos/tduehr/cas/issues/events{/number}","events_url":"https://api.github.com/repos/tduehr/cas/events","assignees_url":"https://api.github.com/repos/tduehr/cas/assignees{/user}","branches_url":"https://api.github.com/repos/tduehr/cas/branches{/branch}","tags_url":"https://api.github.com/repos/tduehr/cas/tags","blobs_url":"https://api.github.com/repos/tduehr/cas/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tduehr/cas/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tduehr/cas/git/refs{/sha}","trees_url":"https://api.github.com/repos/tduehr/cas/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tduehr/cas/statuses/{sha}","languages_url":"https://api.github.com/repos/tduehr/cas/languages","stargazers_url":"https://api.github.com/repos/tduehr/cas/stargazers","contributors_url":"https://api.github.com/repos/tduehr/cas/contributors","subscribers_url":"https://api.github.com/repos/tduehr/cas/subscribers","subscription_url":"https://api.github.com/repos/tduehr/cas/subscription","commits_url":"https://api.github.com/repos/tduehr/cas/commits{/sha}","git_commits_url":"https://api.github.com/repos/tduehr/cas/git/commits{/sha}","comments_url":"https://api.github.com/repos/tduehr/cas/comments{/number}","issue_comment_url":"https://api.github.com/repos/tduehr/cas/issues/comments{/number}","contents_url":"https://api.github.com/repos/tduehr/cas/contents/{+path}","compare_url":"https://api.github.com/repos/tduehr/cas/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tduehr/cas/merges","archive_url":"https://api.github.com/repos/tduehr/cas/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tduehr/cas/downloads","issues_url":"https://api.github.com/repos/tduehr/cas/issues{/number}","pulls_url":"https://api.github.com/repos/tduehr/cas/pulls{/number}","milestones_url":"https://api.github.com/repos/tduehr/cas/milestones{/number}","notifications_url":"https://api.github.com/repos/tduehr/cas/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tduehr/cas/labels{/name}","releases_url":"https://api.github.com/repos/tduehr/cas/releases{/id}","created_at":"2015-09-04T20:49:34Z","updated_at":"2015-09-04T20:49:45Z","pushed_at":"2015-12-02T17:42:51Z","git_url":"git://github.com/tduehr/cas.git","ssh_url":"git@github.com:tduehr/cas.git","clone_url":"https://github.com/tduehr/cas.git","svn_url":"https://github.com/tduehr/cas","homepage":"http://jasig.github.io/cas/","size":552282,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Jasig:master","ref":"master","sha":"bb4c71431a76f49b7ae8b8a19dcc230fdb33b98e","user":{"login":"Jasig","id":884285,"avatar_url":"https://avatars.githubusercontent.com/u/884285?v=3","gravatar_id":"","url":"https://api.github.com/users/Jasig","html_url":"https://github.com/Jasig","followers_url":"https://api.github.com/users/Jasig/followers","following_url":"https://api.github.com/users/Jasig/following{/other_user}","gists_url":"https://api.github.com/users/Jasig/gists{/gist_id}","starred_url":"https://api.github.com/users/Jasig/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Jasig/subscriptions","organizations_url":"https://api.github.com/users/Jasig/orgs","repos_url":"https://api.github.com/users/Jasig/repos","events_url":"https://api.github.com/users/Jasig/events{/privacy}","received_events_url":"https://api.github.com/users/Jasig/received_events","type":"Organization","site_admin":false},"repo":{"id":2352744,"name":"cas","full_name":"Jasig/cas","owner":{"login":"Jasig","id":884285,"avatar_url":"https://avatars.githubusercontent.com/u/884285?v=3","gravatar_id":"","url":"https://api.github.com/users/Jasig","html_url":"https://github.com/Jasig","followers_url":"https://api.github.com/users/Jasig/followers","following_url":"https://api.github.com/users/Jasig/following{/other_user}","gists_url":"https://api.github.com/users/Jasig/gists{/gist_id}","starred_url":"https://api.github.com/users/Jasig/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Jasig/subscriptions","organizations_url":"https://api.github.com/users/Jasig/orgs","repos_url":"https://api.github.com/users/Jasig/repos","events_url":"https://api.github.com/users/Jasig/events{/privacy}","received_events_url":"https://api.github.com/users/Jasig/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Jasig/cas","description":"Apereo CAS - Single Sign On for the Web","fork":false,"url":"https://api.github.com/repos/Jasig/cas","forks_url":"https://api.github.com/repos/Jasig/cas/forks","keys_url":"https://api.github.com/repos/Jasig/cas/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Jasig/cas/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Jasig/cas/teams","hooks_url":"https://api.github.com/repos/Jasig/cas/hooks","issue_events_url":"https://api.github.com/repos/Jasig/cas/issues/events{/number}","events_url":"https://api.github.com/repos/Jasig/cas/events","assignees_url":"https://api.github.com/repos/Jasig/cas/assignees{/user}","branches_url":"https://api.github.com/repos/Jasig/cas/branches{/branch}","tags_url":"https://api.github.com/repos/Jasig/cas/tags","blobs_url":"https://api.github.com/repos/Jasig/cas/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Jasig/cas/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Jasig/cas/git/refs{/sha}","trees_url":"https://api.github.com/repos/Jasig/cas/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Jasig/cas/statuses/{sha}","languages_url":"https://api.github.com/repos/Jasig/cas/languages","stargazers_url":"https://api.github.com/repos/Jasig/cas/stargazers","contributors_url":"https://api.github.com/repos/Jasig/cas/contributors","subscribers_url":"https://api.github.com/repos/Jasig/cas/subscribers","subscription_url":"https://api.github.com/repos/Jasig/cas/subscription","commits_url":"https://api.github.com/repos/Jasig/cas/commits{/sha}","git_commits_url":"https://api.github.com/repos/Jasig/cas/git/commits{/sha}","comments_url":"https://api.github.com/repos/Jasig/cas/comments{/number}","issue_comment_url":"https://api.github.com/repos/Jasig/cas/issues/comments{/number}","contents_url":"https://api.github.com/repos/Jasig/cas/contents/{+path}","compare_url":"https://api.github.com/repos/Jasig/cas/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Jasig/cas/merges","archive_url":"https://api.github.com/repos/Jasig/cas/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Jasig/cas/downloads","issues_url":"https://api.github.com/repos/Jasig/cas/issues{/number}","pulls_url":"https://api.github.com/repos/Jasig/cas/pulls{/number}","milestones_url":"https://api.github.com/repos/Jasig/cas/milestones{/number}","notifications_url":"https://api.github.com/repos/Jasig/cas/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Jasig/cas/labels{/name}","releases_url":"https://api.github.com/repos/Jasig/cas/releases{/id}","created_at":"2011-09-09T01:36:42Z","updated_at":"2015-12-01T02:44:20Z","pushed_at":"2015-12-02T18:31:51Z","git_url":"git://github.com/Jasig/cas.git","ssh_url":"git@github.com:Jasig/cas.git","clone_url":"https://github.com/Jasig/cas.git","svn_url":"https://github.com/Jasig/cas","homepage":"http://jasig.github.io/cas/","size":757370,"stargazers_count":990,"watchers_count":990,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":773,"mirror_url":null,"open_issues_count":52,"forks":773,"open_issues":52,"watchers":990,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Jasig/cas/pulls/1293"},"html":{"href":"https://github.com/Jasig/cas/pull/1293"},"issue":{"href":"https://api.github.com/repos/Jasig/cas/issues/1293"},"comments":{"href":"https://api.github.com/repos/Jasig/cas/issues/1293/comments"},"review_comments":{"href":"https://api.github.com/repos/Jasig/cas/pulls/1293/comments"},"review_comment":{"href":"https://api.github.com/repos/Jasig/cas/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Jasig/cas/pulls/1293/commits"},"statuses":{"href":"https://api.github.com/repos/Jasig/cas/statuses/e63ad7952be30de21b03f819a6fa26ec226dc776"}}}}