package geniusweb.partiesserver.websocket; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import javax.management.MalformedObjectNameException; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import geniusweb.partiesserver.Constants; import geniusweb.partiesserver.repository.AvailablePartiesRepo; import geniusweb.partiesserver.repository.AvailableParty; import geniusweb.partiesserver.repository.GeneralPartyInfo; import tudelft.utilities.listener.Listener; import tudelft.utilities.logging.ReportToLogger; import tudelft.utilities.logging.Reporter; /** * Returns a websocket that communicates the list of currently available * (runnable) parties. Every time something changes, a new list of parties is * sent. */ @ServerEndpoint("/available") public class PartiesListSocket { private static final String DEFAULT_ADDRESS = "localhost:8080"; private final Reporter log; private Listener changeListener; // final, but only initialized in // startSession. private Session session; private AvailablePartiesRepo repo; private SendBuffer outstream; public PartiesListSocket() { this(new ReportToLogger("partiesserver")); } public PartiesListSocket(Reporter reporter) { if (reporter == null) throw new IllegalArgumentException("reporter must be not null"); this.log = reporter; } @OnOpen public void start(Session session) throws IOException { this.session = session; this.outstream = new SendBuffer(session.getBasicRemote(), log); repo = getRepo(); changeListener = new Listener() { @Override public void notifyChange(String data) { sendUpdatedParties(); } }; repo.addListener(changeListener); sendUpdatedParties(); } /** * Send the currently available parties to the client. * * @throws IOException */ private void sendUpdatedParties() { log.log(Level.INFO, "sending updated parties list1"); String ip = DEFAULT_ADDRESS; // default if something fails try { ip = Constants.getIpAddressAndPort() + "/" + getServerName(); } catch (MalformedObjectNameException | UnknownHostException e1) { log.log(Level.SEVERE, "Can't get proper server name and port, reverting to " + DEFAULT_ADDRESS, e1); } try { List list = new LinkedList<>(); for (AvailableParty party : repo.list()) { URI uri = new URI("http://" + ip + "/run/" + party.getName()); list.add(new GeneralPartyInfo(uri, party.getCapabilities(), party.getDescription())); } String text = Constants.getJackson().writeValueAsString(list); outstream.send(text); } catch (IOException | URISyntaxException e) { log.log(Level.SEVERE, "Bug sending updated parties list", e); } } @OnClose public void onClose() throws IOException { repo.removeListener(changeListener); outstream.stop(); } @OnError public void onError(Throwable t) throws Throwable { if (t instanceof IOException && "Broken pipe".equals(t.getMessage())) { log.log(Level.INFO, "Socket pipe broke:" + session); } else { log.log(Level.SEVERE, "Tomcat reported an error on the connection:" + session, t); } } /** * FactoryMethod for testing * * @return RunningPartiesRepo */ protected AvailablePartiesRepo getRepo() { return AvailablePartiesRepo.instance(); } /** * * @return name of our service, eg "profilesserver" */ private String getServerName() { // typically something like /profilesserver/websocket/liststream // profilesserver is the name we're looking for return session.getRequestURI().getPath().split("/")[1]; // drop leading // '/' } }