001package jmri.server.json.layoutblock; 002 003import static jmri.server.json.JSON.NAME; 004import static jmri.server.json.JSON.PUT; 005import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCK; 006import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCKS; 007 008import com.fasterxml.jackson.databind.JsonNode; 009import java.beans.PropertyChangeEvent; 010import java.beans.PropertyChangeListener; 011import java.io.IOException; 012import java.util.HashMap; 013import java.util.HashSet; 014import jmri.InstanceManager; 015import jmri.JmriException; 016import jmri.jmrit.display.layoutEditor.LayoutBlock; 017import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 018import jmri.server.json.JSON; 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 * 028 * @author mstevetodd Copyright (C) 2018 029 * @author Randall Wood 030 */ 031public class JsonLayoutBlockSocketService extends JsonSocketService<JsonLayoutBlockHttpService> { 032 033 private final HashMap<LayoutBlock, LayoutBlockListener> layoutBlockListeners = new HashMap<>(); 034 private final LayoutBlocksListener layoutBlocksListener = new LayoutBlocksListener(); 035 private static final Logger log = LoggerFactory.getLogger(JsonLayoutBlockSocketService.class); 036 037 public JsonLayoutBlockSocketService(JsonConnection connection) { 038 super(connection, new JsonLayoutBlockHttpService(connection.getObjectMapper())); 039 } 040 041 @Override 042 public void onMessage(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException { 043 String name = data.path(NAME).asText(); 044 LayoutBlock layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(name); 045 if (!request.method.equals(PUT) && layoutBlock != null && !layoutBlock.getSystemName().equals(name)) { 046 name = layoutBlock.getSystemName(); 047 } 048 switch (request.method) { 049 case JSON.DELETE: 050 service.doDelete(type, name, data, request); 051 break; 052 case JSON.POST: 053 connection.sendMessage(service.doPost(type, name, data, request), request.id); 054 break; 055 case JSON.PUT: 056 connection.sendMessage(service.doPut(type, name, data, request), request.id); 057 break; 058 default: 059 case JSON.GET: 060 connection.sendMessage(service.doGet(type, name, data, request), request.id); 061 break; 062 } 063 layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(name); 064 if (layoutBlock != null && !layoutBlockListeners.containsKey(layoutBlock)) { 065 LayoutBlockListener listener = new LayoutBlockListener(layoutBlock); 066 layoutBlock.addPropertyChangeListener(listener); 067 layoutBlockListeners.put(layoutBlock, listener); 068 } 069 } 070 071 @Override 072 public void onList(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException { 073 connection.sendMessage(service.doGetList(type, data, request), request.id); 074 log.debug("adding LayoutBlocksListener"); 075 InstanceManager.getDefault(LayoutBlockManager.class).addPropertyChangeListener(layoutBlocksListener); //add parent listener 076 } 077 078 @Override 079 public void onClose() { 080 layoutBlockListeners.values().stream().forEach(layoutblock -> 081 layoutblock.layoutBlock.removePropertyChangeListener(layoutblock)); 082 layoutBlockListeners.clear(); 083 } 084 085 private class LayoutBlockListener implements PropertyChangeListener { 086 087 protected final LayoutBlock layoutBlock; 088 089 public LayoutBlockListener(LayoutBlock layoutblock) { 090 layoutBlock = layoutblock; 091 } 092 093 @Override 094 public void propertyChange(PropertyChangeEvent e) { 095 if (e.getPropertyName().equals("redraw")) { 096 log.debug("{} property '{}' changed from '{}' to '{}'", layoutBlock.getUserName(), 097 e.getPropertyName(), e.getOldValue(), e.getNewValue()); 098 try { 099 try { 100 connection.sendMessage(service.doGet(LAYOUTBLOCK, layoutBlock.getSystemName(), connection.getObjectMapper().createObjectNode(), new JsonRequest(getLocale(), getVersion(), JSON.GET, 0)), 0); 101 } catch (JsonException ex) { 102 connection.sendMessage(ex.getJsonMessage(), 0); 103 } 104 } catch (IOException ex) { 105 // if we get an error, de-register 106 layoutBlock.removePropertyChangeListener(this); 107 layoutBlockListeners.remove(layoutBlock); 108 } 109 } 110 } 111 } 112 113 private class LayoutBlocksListener implements PropertyChangeListener { 114 @Override 115 public void propertyChange(PropertyChangeEvent evt) { 116 log.debug("in LayoutBlocksListener for '{}' ('{}' => '{}')", evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); 117 118 try { 119 try { 120 // send the new list 121 connection.sendMessage(service.doGetList(LAYOUTBLOCKS, service.getObjectMapper().createObjectNode(), new JsonRequest(getLocale(), getVersion(), JSON.GET, 0)), 0); 122 //child added or removed, reset listeners 123 if (evt.getPropertyName().equals("length")) { // NOI18N 124 removeListenersFromRemovedBeans(); 125 } 126 } catch (JsonException ex) { 127 log.warn("json error sending LayoutBlocks: {}", ex.getJsonMessage()); 128 connection.sendMessage(ex.getJsonMessage(), 0); 129 } 130 } catch (IOException ex) { 131 // if we get an error, de-register 132 log.debug("deregistering layoutBlocksListener due to IOException"); 133 InstanceManager.getDefault(LayoutBlockManager.class).removePropertyChangeListener(layoutBlocksListener); 134 } 135 } 136 137 private void removeListenersFromRemovedBeans() { 138 for (LayoutBlock layoutBlock : new HashSet<>(layoutBlockListeners.keySet())) { 139 if (InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(layoutBlock.getSystemName()) == null) { 140 layoutBlockListeners.remove(layoutBlock); 141 } 142 } 143 } 144 } 145 146}