jaroslav@238: /** jaroslav@238: * The MIT License (MIT) jaroslav@238: * jaroslav@238: * Copyright (C) 2013 Jaroslav Tulach jaroslav@238: * jaroslav@238: * Permission is hereby granted, free of charge, to any person obtaining a copy jaroslav@238: * of this software and associated documentation files (the "Software"), to deal jaroslav@238: * in the Software without restriction, including without limitation the rights jaroslav@238: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell jaroslav@238: * copies of the Software, and to permit persons to whom the Software is jaroslav@238: * furnished to do so, subject to the following conditions: jaroslav@238: * jaroslav@238: * The above copyright notice and this permission notice shall be included in jaroslav@238: * all copies or substantial portions of the Software. jaroslav@238: * jaroslav@238: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR jaroslav@238: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, jaroslav@238: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE jaroslav@238: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER jaroslav@238: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, jaroslav@238: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN jaroslav@238: * THE SOFTWARE. jaroslav@238: */ jaroslav@238: package org.apidesign.demo.chat.server; jaroslav@238: jaroslav@238: import java.util.ArrayList; jaroslav@238: import java.util.IdentityHashMap; jaroslav@238: import java.util.List; jaroslav@238: import java.util.Map; jaroslav@238: import java.util.logging.Logger; jaroslav@238: import javax.inject.Singleton; jaroslav@238: import javax.ws.rs.Consumes; jaroslav@238: import javax.ws.rs.DefaultValue; jaroslav@238: import javax.ws.rs.GET; jaroslav@238: import javax.ws.rs.POST; jaroslav@238: import javax.ws.rs.Path; jaroslav@238: import javax.ws.rs.Produces; jaroslav@238: import javax.ws.rs.QueryParam; jaroslav@238: import javax.ws.rs.container.AsyncResponse; jaroslav@238: import javax.ws.rs.container.Suspended; jaroslav@238: import javax.ws.rs.core.MediaType; jaroslav@238: import org.apidesign.demo.chat.shared.Message; jaroslav@238: import org.apidesign.demo.chat.shared.Query; jaroslav@238: jaroslav@238: /** Server side of the chat application.*/ jaroslav@238: @Path("/chat/") @Singleton jaroslav@238: public final class ChatServerResource { jaroslav@238: private static final Logger LOG = Logger.getLogger(ChatServerResource.class.getName()); jaroslav@238: private static final long started = System.currentTimeMillis() - 10; jaroslav@238: jaroslav@238: private List msgs = new ArrayList<>(); jaroslav@238: { jaroslav@238: Message welcome = new Message(); jaroslav@238: welcome.setUser("system"); jaroslav@238: welcome.setComment("Welcome and enjoy!"); jaroslav@238: welcome.setSince(10); jaroslav@238: msgs.add(welcome); jaroslav@238: } jaroslav@238: jaroslav@238: private final Map awaiting = new IdentityHashMap<>(); jaroslav@238: jaroslav@238: @Produces(MediaType.APPLICATION_JSON) jaroslav@238: @GET public synchronized void getResources( jaroslav@238: @QueryParam("since") @DefaultValue("0") long since, jaroslav@238: @Suspended AsyncResponse ar jaroslav@238: ) { jaroslav@238: Query q = new Query(); jaroslav@238: for (Message m : msgs) { jaroslav@238: if (m.getSince()>= since) { jaroslav@238: q.getMessages().add(m); jaroslav@238: } jaroslav@238: } jaroslav@238: if (!q.getMessages().isEmpty()) { jaroslav@238: ar.resume(q); jaroslav@238: } else { jaroslav@238: awaiting.put(ar, since); jaroslav@238: } jaroslav@238: } jaroslav@238: jaroslav@238: private void handleAwaiting(long newest) { jaroslav@238: assert Thread.holdsLock(this); jaroslav@238: AGAIN: for (;;) { jaroslav@238: for (Map.Entry entry : awaiting.entrySet()) { jaroslav@238: AsyncResponse ar = entry.getKey(); jaroslav@238: Long since = entry.getValue(); jaroslav@238: if (since <= newest) { jaroslav@238: awaiting.remove(ar); jaroslav@238: getResources(since, ar); jaroslav@238: continue AGAIN; jaroslav@238: } jaroslav@238: } jaroslav@238: return; jaroslav@238: } jaroslav@238: } jaroslav@238: jaroslav@238: @POST @Consumes(value = MediaType.APPLICATION_JSON) jaroslav@238: public synchronized Message publish(Message msg) { jaroslav@238: msg.setSince(System.currentTimeMillis() - started); jaroslav@238: msgs.add(msg); jaroslav@238: handleAwaiting(msg.getSince()); jaroslav@238: return msg; jaroslav@238: } jaroslav@238: }