001package jmri.web.servlet.operations; 002 003import static jmri.web.servlet.ServletUtil.*; 004 005import java.io.IOException; 006 007import javax.servlet.ServletException; 008import javax.servlet.annotation.WebServlet; 009import javax.servlet.http.*; 010 011import org.apache.commons.text.StringEscapeUtils; 012import org.openide.util.lookup.ServiceProvider; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016import com.fasterxml.jackson.databind.JsonNode; 017import com.fasterxml.jackson.databind.ObjectMapper; 018import com.fasterxml.jackson.databind.node.ObjectNode; 019 020import jmri.InstanceManager; 021import jmri.jmrit.operations.OperationsManager; 022import jmri.jmrit.operations.rollingstock.cars.CarManager; 023import jmri.jmrit.operations.setup.Setup; 024import jmri.jmrit.operations.trains.*; 025import jmri.server.json.JSON; 026import jmri.server.json.operations.JsonOperations; 027import jmri.server.json.operations.JsonUtil; 028import jmri.util.FileUtil; 029import jmri.web.server.WebServer; 030import jmri.web.servlet.ServletUtil; 031 032/** 033 * 034 * @author Randall Wood (C) 2014 035 * @author Steve Todd (C) 2013 036 */ 037@WebServlet(name = "OperationsServlet", 038 urlPatterns = { 039 "/operations", // default 040 "/web/operationsConductor.html", // redirect to default since ~ 13 May 2014 041 "/web/operationsManifest.html", // redirect to default since ~ 13 May 2014 042 "/web/operationsTrains.html" // redirect to default since ~ 13 May 2014 043 }) 044@ServiceProvider(service = HttpServlet.class) 045public class OperationsServlet extends HttpServlet { 046 047 private ObjectMapper mapper; 048 049 private final static Logger log = LoggerFactory.getLogger(OperationsServlet.class); 050 051 @Override 052 public void init() throws ServletException { 053 // only do complete initialization for default path, not redirections 054 if (this.getServletContext().getContextPath().equals("/operations")) { // NOI18N 055 this.mapper = new ObjectMapper(); 056 // ensure all operations managers are functional before handling first request 057 InstanceManager.getDefault(OperationsManager.class); 058 } 059 } 060 061 /* 062 * Valid paths are: 063 * /operations/trains -or- /operations - get a list of trains for operations 064 * /operations/manifest/id - get the manifest for train with Id "id" 065 * /operations/conductor/id - get the conductor's screen for train with Id "id" 066 */ 067 protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 068 if (request.getRequestURI().equals("/web/operationsConductor.html") // NOI18N 069 || request.getRequestURI().equals("/web/operationsManifest.html") // NOI18N 070 || request.getRequestURI().equals("/web/operationsTrains.html")) { // NOI18N 071 response.sendRedirect("/operations"); // NOI18N 072 return; 073 } 074 String[] pathInfo = request.getPathInfo().substring(1).split("/"); 075 response.setHeader("Connection", "Keep-Alive"); // NOI18N 076 if (pathInfo[0].equals("") || (pathInfo[0].equals(JsonOperations.TRAINS) && pathInfo.length == 1)) { 077 this.processTrains(request, response); 078 } else { 079 if (pathInfo.length == 1) { 080 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 081 } else { 082 String id = pathInfo[1]; 083 String report = pathInfo[0]; 084 if (report.equals(JsonOperations.TRAINS) && pathInfo.length == 3) { 085 report = pathInfo[2]; 086 } 087 log.debug("Handling {} with id {}", report, id); 088 switch (report) { 089 case "manifest": 090 this.processManifest(id, request, response); 091 break; 092 case "conductor": 093 this.processConductor(id, request, response); 094 break; 095 case "trains": 096 // TODO: allow for editing/building/reseting train 097 log.warn("Unhandled request for \"trains\""); 098 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 099 break; 100 default: 101 // Don't know what to do 102 log.warn("Unparsed request for \"{}\"", report); 103 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 104 break; 105 } 106 } 107 } 108 } 109 110 protected void processTrains(HttpServletRequest request, HttpServletResponse response) throws IOException { 111 if (JSON.JSON.equals(request.getParameter("format"))) { 112 response.setContentType(UTF8_APPLICATION_JSON); 113 InstanceManager.getDefault(ServletUtil.class).setNonCachingHeaders(response); 114 JsonUtil utilities = new JsonUtil(this.mapper); 115 response.getWriter().print(utilities.getTrains(request.getLocale())); 116 } else if ("html".equals(request.getParameter("format"))) { 117 response.setContentType(UTF8_TEXT_HTML); 118 InstanceManager.getDefault(ServletUtil.class).setNonCachingHeaders(response); 119 boolean showAll = ("all".equals(request.getParameter("show"))); 120 StringBuilder html = new StringBuilder(); 121 String format = FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "TrainsSnippet.html"))); 122 for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) { 123 if (showAll || !InstanceManager.getDefault(CarManager.class).getByTrainDestinationList(train).isEmpty()) { 124 html.append(String.format(request.getLocale(), format, 125 train.getIconName(), 126 StringEscapeUtils.escapeHtml4(train.getDescription()), 127 train.getLeadEngine() != null ? train.getLeadEngine().toString() : "", 128 StringEscapeUtils.escapeHtml4(train.getTrainDepartsName()), 129 train.getDepartureTime(), 130 train.getStatus(), 131 StringEscapeUtils.escapeHtml4(train.getCurrentLocationName()), 132 StringEscapeUtils.escapeHtml4(train.getTrainTerminatesName()), 133 StringEscapeUtils.escapeHtml4(train.getTrainRouteName()), 134 train.getId() 135 )); 136 } 137 } 138 response.getWriter().print(html.toString()); 139 } else { 140 response.setContentType(UTF8_TEXT_HTML); 141 response.getWriter().print(String.format(request.getLocale(), 142 FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "Operations.html"))), 143 String.format(request.getLocale(), 144 Bundle.getMessage(request.getLocale(), "HtmlTitle"), 145 InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 146 Bundle.getMessage(request.getLocale(), "TrainsTitle") 147 ), 148 InstanceManager.getDefault(ServletUtil.class).getNavBar(request.getLocale(), request.getContextPath()), 149 InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 150 InstanceManager.getDefault(ServletUtil.class).getFooter(request.getLocale(), request.getContextPath()), 151 "" // no train Id 152 )); 153 } 154 } 155 156 private void processManifest(String id, HttpServletRequest request, HttpServletResponse response) throws IOException { 157 Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id); 158 if ("html".equals(request.getParameter("format"))) { 159 log.debug("Getting manifest HTML code for train {}", id); 160 HtmlManifest manifest = new HtmlManifest(request.getLocale(), train); 161 InstanceManager.getDefault(ServletUtil.class).setNonCachingHeaders(response); 162 response.setContentType(UTF8_TEXT_HTML); 163 response.getWriter().print(String.format(request.getLocale(), 164 FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "ManifestSnippet.html"))), 165 train.getIconName(), 166 StringEscapeUtils.escapeHtml4(train.getDescription()), 167 Setup.isPrintValidEnabled() ? manifest.getValidity() : "", 168 HtmlTrainCommon.convertToHTMLColor(StringEscapeUtils.escapeHtml4(train.getCommentWithColor())), 169 Setup.isPrintRouteCommentsEnabled() ? train.getRoute().getComment() : "", 170 HtmlTrainCommon.convertToHTMLColor(manifest.getLocations()) 171 )); 172 } else if (JSON.JSON.equals(request.getParameter("format"))) { 173 log.debug("Getting manifest JSON code for train {}", id); 174 JsonNode manifest = this.mapper.readTree(new JsonManifest(train).getFile()); 175 if (manifest.path(JSON.IMAGE).isTextual()) { 176 ((ObjectNode) manifest).put(JSON.IMAGE, WebServer.portablePathToURI(FileUtil.getPortableFilename(manifest.path(JSON.IMAGE).asText()))); 177 } 178 String content = this.mapper.writeValueAsString(manifest); 179 response.setContentType(ServletUtil.UTF8_APPLICATION_JSON); 180 response.setContentLength(content.getBytes(UTF8).length); 181 response.getWriter().print(content); 182 } else { 183 response.setContentType(UTF8_TEXT_HTML); 184 response.getWriter().print(String.format(request.getLocale(), 185 FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "Operations.html"))), 186 String.format(request.getLocale(), 187 Bundle.getMessage(request.getLocale(), "HtmlTitle"), 188 InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 189 String.format(request.getLocale(), 190 Bundle.getMessage(request.getLocale(), "ManifestTitle"), 191 train.getIconName(), 192 StringEscapeUtils.escapeHtml4(train.getDescription()) 193 ) 194 ), 195 InstanceManager.getDefault(ServletUtil.class).getNavBar(request.getLocale(), request.getContextPath()), 196 !train.getRailroadName().equals("") ? train.getRailroadName() : InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 197 InstanceManager.getDefault(ServletUtil.class).getFooter(request.getLocale(), request.getContextPath()), 198 train.getId() 199 )); 200 } 201 } 202 203 private void processConductor(String id, HttpServletRequest request, HttpServletResponse response) throws IOException { 204 Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id); 205 JsonNode data; 206 if (request.getContentType() != null && request.getContentType().contains(APPLICATION_JSON)) { 207 data = this.mapper.readTree(request.getReader()); 208 if (!data.path(JSON.DATA).isMissingNode()) { 209 data = data.path(JSON.DATA); 210 } 211 } else { 212 data = this.mapper.createObjectNode(); 213 ((ObjectNode) data).put("format", request.getParameter("format")); 214 } 215 if (data.path("format").asText().equals("html")) { 216 JsonNode location = data.path(JsonOperations.LOCATION); 217 if (!location.isMissingNode()) { 218 if (location.isNull() || train.getNextLocationName().equals(location.asText())) { 219 train.move(); 220 return; // done; property change will cause update to client 221 } 222 } 223 log.debug("Getting conductor HTML code for train {}", id); 224 HtmlConductor conductor = new HtmlConductor(request.getLocale(), train); 225 InstanceManager.getDefault(ServletUtil.class).setNonCachingHeaders(response); 226 response.setContentType(UTF8_TEXT_HTML); 227 response.getWriter().print(conductor.getLocation()); 228 } else { 229 response.setContentType(UTF8_TEXT_HTML); 230 response.getWriter().print(String.format(request.getLocale(), 231 FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "Operations.html"))), 232 String.format(request.getLocale(), 233 Bundle.getMessage(request.getLocale(), "HtmlTitle"), 234 InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 235 String.format(request.getLocale(), 236 Bundle.getMessage(request.getLocale(), "ConductorTitle"), 237 train.getIconName(), 238 StringEscapeUtils.escapeHtml4(train.getDescription()) 239 ) 240 ), 241 InstanceManager.getDefault(ServletUtil.class).getNavBar(request.getLocale(), request.getContextPath()), 242 !train.getRailroadName().equals("") ? train.getRailroadName() : InstanceManager.getDefault(ServletUtil.class).getRailroadName(false), 243 InstanceManager.getDefault(ServletUtil.class).getFooter(request.getLocale(), request.getContextPath()), 244 train.getId() 245 )); 246 } 247 } 248// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code."> 249 250 /** 251 * Handles the HTTP <code>GET</code> method. 252 * 253 * @param request servlet request 254 * @param response servlet response 255 * @throws ServletException if a servlet-specific error occurs 256 * @throws IOException if an I/O error occurs 257 */ 258 @Override 259 protected void doGet(HttpServletRequest request, HttpServletResponse response) 260 throws ServletException, IOException { 261 processRequest(request, response); 262 } 263 264 /** 265 * Handles the HTTP <code>POST</code> method. 266 * 267 * @param request servlet request 268 * @param response servlet response 269 * @throws ServletException if a servlet-specific error occurs 270 * @throws IOException if an I/O error occurs 271 */ 272 @Override 273 protected void doPost(HttpServletRequest request, HttpServletResponse response) 274 throws ServletException, IOException { 275 processRequest(request, response); 276 } 277 278 /** 279 * Handles the HTTP <code>PUT</code> method. 280 * 281 * @param request servlet request 282 * @param response servlet response 283 * @throws ServletException if a servlet-specific error occurs 284 * @throws IOException if an I/O error occurs 285 */ 286 @Override 287 protected void doPut(HttpServletRequest request, HttpServletResponse response) 288 throws ServletException, IOException { 289 processRequest(request, response); 290 } 291 292 /** 293 * Returns a short description of the servlet. 294 * 295 * @return a String containing servlet description 296 */ 297 @Override 298 public String getServletInfo() { 299 return "Operations Servlet"; 300 }// </editor-fold> 301 302}