package geniusweb.runserver; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import geniusweb.protocol.partyconnection.ProtocolToPartyConn; import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory; import geniusweb.references.PartyRef; import geniusweb.references.Reference; import tudelft.utilities.logging.Reporter; import tudelft.utilities.repository.NoResourcesNowException; /** * Factory conform ConnectionWithPartyFactory based on HTTP calls and websockets * following the partiesserver protocols. */ class PartyConnectionFactory implements ProtocolToPartyConnFactory { private final Reporter log; /** * @param logger the logger used for all created connections as well as for * failed attempts to connect. */ public PartyConnectionFactory(Reporter logger) { this.log = logger; } @Override public WebSocketProtToPartyConn connect(Reference reference) throws IOException, NoResourcesNowException { try { return new WebSocketProtToPartyConn(reference, log).init(); } catch (IOException e) { Date date = getWaitingTime(e); if (date == null) throw e; throw new NoResourcesNowException("No resources for " + reference, date); } } /** * This is a specific implementation that uses a simple modified Banker's * algorithm. * * @throws NoResourcesNowException if one party can not be allocated, * failing with a * {@link NoResourcesNowException}. In this * case all resources that were allocated * while the attempt was done are freed up. */ @Override public List connect(List references) throws IOException, NoResourcesNowException { /* * FIXME Check first. If request is impossible, we should throw a * ProtocolException instead of keeping failing with * NoResourcesNowException. This implementation will cause a life lock * in a session or tournament with impossible settings (eg more parties * than slots on the partiesserver). */ log.log(Level.INFO, "attempting to connect " + references); List connections = new LinkedList<>(); try { for (Reference partyref : references) { ProtocolToPartyConn conn = null; while (conn == null) { try { conn = connect(partyref); } catch (IOException e) { throw new IOException( "Failed to connect to " + partyref.toString(), e); } } connections.add(conn); } } catch (IOException | NoResourcesNowException e) { // free all already created connections for (ProtocolToPartyConn conn : connections) { conn.close(); } throw e; } log.log(Level.INFO, "succesfully connected " + references); return connections; } /** * Try to get a suggested wait time out of the IOException. * * Sometiems the IOException itself contains already the text "please retry * later". * * * @param e the {@link IOException} that may contain extra information * @return a Date contained in the IOException */ private Date getWaitingTime(IOException e) { String msg = e.getMessage(); if (msg.contains("retry later")) { long suggestedRetryTime = -1; try { String[] elements = msg.split(" "); suggestedRetryTime = Long .parseLong(elements[elements.length - 1]); } catch (NumberFormatException e1) { // silent catch, we check the value and report errors below. } if (suggestedRetryTime < 0) { log.log(Level.WARNING, "Server 503 message does not contain proper positive long as last value:" + msg, e); suggestedRetryTime = System.currentTimeMillis() + 5000; } return new Date(suggestedRetryTime); } return null; } private Map> groupByServer(List references) { Map> map = new HashMap<>(); for (Reference ref : references) { } return map; } /** * Get the base uri of a Reference (http://base./../..) from a ref like * http://base../run/ * * @param ref the reference to extract base URI from * @return URI holding the base name of the server */ private URI getBaseURI(PartyRef ref) { String str = ref.getURI().toString(); try { return new URI(str.substring(0, str.indexOf("/run/"))); } catch (URISyntaxException e) { e.printStackTrace(); // straight bug return null; } } }