001package jmri.server.json.signalhead; 002 003import static jmri.server.json.JSON.GET; 004import static jmri.server.json.JSON.NAME; 005import static jmri.server.json.JSON.PUT; 006import static jmri.server.json.signalhead.JsonSignalHead.SIGNAL_HEAD; 007import static jmri.server.json.signalhead.JsonSignalHead.SIGNAL_HEADS; 008 009import com.fasterxml.jackson.databind.JsonNode; 010import java.beans.PropertyChangeEvent; 011import java.beans.PropertyChangeListener; 012import java.io.IOException; 013import java.util.HashMap; 014import java.util.HashSet; 015import jmri.InstanceManager; 016import jmri.JmriException; 017import jmri.SignalHead; 018import jmri.SignalHeadManager; 019import jmri.server.json.JsonConnection; 020import jmri.server.json.JsonException; 021import jmri.server.json.JsonRequest; 022import jmri.server.json.JsonSocketService; 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026/** 027 * @author Randall Wood (C) 2016 028 */ 029public class JsonSignalHeadSocketService extends JsonSocketService<JsonSignalHeadHttpService> { 030 031 private final HashMap<String, SignalHeadListener> beanListeners = new HashMap<>(); 032 private SignalHeadsListener managerListener = null; 033 private static final Logger log = LoggerFactory.getLogger(JsonSignalHeadSocketService.class); 034 035 public JsonSignalHeadSocketService(JsonConnection connection) { 036 this(connection, new JsonSignalHeadHttpService(connection.getObjectMapper())); 037 } 038 039 // package protected 040 public JsonSignalHeadSocketService(JsonConnection connection, JsonSignalHeadHttpService service) { 041 super(connection, service); 042 } 043 044 @Override 045 public void onMessage(String type, JsonNode data, JsonRequest request) 046 throws IOException, JmriException, JsonException { 047 String name = data.path(NAME).asText(); 048 if (request.method.equals(PUT)) { 049 connection.sendMessage(service.doPut(type, name, data, request), request.id); 050 } else { 051 connection.sendMessage(service.doPost(type, name, data, request), request.id); 052 } 053 if (!beanListeners.containsKey(name)) { 054 SignalHead signalHead = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(name); 055 if (signalHead != null) { 056 SignalHeadListener listener = new SignalHeadListener(signalHead); 057 signalHead.addPropertyChangeListener(listener); 058 beanListeners.put(name, listener); 059 } 060 } 061 } 062 063 @Override 064 public void onList(String type, JsonNode data, JsonRequest request) 065 throws IOException, JmriException, JsonException { 066 connection.sendMessage(service.doGetList(type, data, request), request.id); 067 log.debug("adding SignalHeadsListener"); 068 if (managerListener == null) { 069 managerListener = new SignalHeadsListener(); 070 InstanceManager.getDefault(SignalHeadManager.class).addPropertyChangeListener(managerListener); 071 } 072 } 073 074 @Override 075 public void onClose() { 076 beanListeners.values().stream() 077 .forEach(listener -> listener.signalHead.removePropertyChangeListener(listener)); 078 beanListeners.clear(); 079 InstanceManager.getDefault(SignalHeadManager.class).removePropertyChangeListener(managerListener); 080 managerListener = null; 081 } 082 083 private class SignalHeadListener implements PropertyChangeListener { 084 085 protected final SignalHead signalHead; 086 087 public SignalHeadListener(SignalHead signalHead) { 088 this.signalHead = signalHead; 089 } 090 091 @Override 092 public void propertyChange(PropertyChangeEvent e) { 093 try { 094 try { 095 connection.sendMessage(service.doGet(SIGNAL_HEAD, signalHead.getSystemName(), 096 connection.getObjectMapper().createObjectNode(), new JsonRequest(getLocale(), getVersion(), GET, 0)), 0); 097 } catch (JsonException ex) { 098 connection.sendMessage(ex.getJsonMessage(), 0); 099 } 100 } catch (IOException ie) { 101 // if we get an error, de-register 102 signalHead.removePropertyChangeListener(this); 103 beanListeners.remove(signalHead.getSystemName()); 104 } 105 } 106 } 107 108 private class SignalHeadsListener implements PropertyChangeListener { 109 @Override 110 public void propertyChange(PropertyChangeEvent evt) { 111 log.debug("in SignalHeadsListener for '{}' ('{}' => '{}')", evt.getPropertyName(), evt.getOldValue(), 112 evt.getNewValue()); 113 114 try { 115 try { 116 // send the new list 117 connection.sendMessage(service.doGetList(SIGNAL_HEADS, service.getObjectMapper().createObjectNode(), 118 new JsonRequest(getLocale(), getVersion(), GET, 0)), 0); 119 // child added or removed, reset listeners 120 if (evt.getPropertyName().equals("length")) { // NOI18N 121 removeListenersFromRemovedBeans(); 122 } 123 } catch (JsonException ex) { 124 log.warn("json error sending SignalHeads: {}", ex.getJsonMessage()); 125 connection.sendMessage(ex.getJsonMessage(), 0); 126 } 127 } catch (IOException ex) { 128 // do nothing; the listeners will be removed as the connection 129 // gets closed 130 } 131 } 132 133 private void removeListenersFromRemovedBeans() { 134 SignalHeadManager manager = InstanceManager.getDefault(SignalHeadManager.class); 135 for (String name : new HashSet<>(beanListeners.keySet())) { 136 if (manager.getBySystemName(name) == null) { 137 beanListeners.remove(name); 138 } 139 } 140 } 141 } 142 143}