Java tutorial
/** * Copyright (c) 2010-2019 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0 * * SPDX-License-Identifier: EPL-2.0 */ package org.openhab.io.neeo.internal; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventFilter; import org.openhab.io.neeo.NeeoService; import org.openhab.io.neeo.internal.servletservices.ServletService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This implementation of {@link HttpServlet} handles all the routing for the servlet. {@link ServletService}'s are * added in the constructor and then delegated to by this class. * * @author Tim Roberts - Initial Contribution */ @NonNullByDefault public abstract class AbstractServlet extends HttpServlet implements AutoCloseable { /** The serial UID */ private static final long serialVersionUID = -9109038869609595306L; /** The logger */ private final Logger logger = LoggerFactory.getLogger(AbstractServlet.class); /** The services for this servlet */ private final ServletService[] services; /** URL of the servlet */ private final String servletUrl; /** Any event filters */ @Nullable private final List<EventFilter> eventFilters; /** * Creates a servlet to serve the status/definitions web pages * * @param context the non-null service context * @param servletUrl the non-null servletUrl * @param services the non-null list of services */ AbstractServlet(ServiceContext context, String servletUrl, ServletService... services) { NeeoUtil.requireNotEmpty(servletUrl, "servletUrl cannot be empty"); Objects.requireNonNull(context, "context cannot be null"); Objects.requireNonNull(services, "services cannot be null"); this.servletUrl = servletUrl; this.services = services; final List<EventFilter> efs = new ArrayList<>(); for (ServletService service : services) { EventFilter ef = service.getEventFilter(); if (ef != null) { efs.add(ef); } } if (efs.isEmpty()) { eventFilters = null; } else { eventFilters = efs; } } /** * Returns the URL the servlet listens on * * @return a non-null, non-empty servlet URL */ public String getServletUrl() { return servletUrl; } /** * Handles the get by routing it to the appropriate {@link ServletService} or logging it if no route found */ @Override protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws ServletException, IOException { Objects.requireNonNull(req, "req cannot be null"); Objects.requireNonNull(resp, "resp cannot be null"); if (logger.isDebugEnabled()) { logger.debug("doGet: {}", getFullURL(req)); } final String pathInfo = NeeoUtil.decodeURIComponent(req.getPathInfo()); // invalid path - probably someone typed the path in manually if (StringUtils.isEmpty(pathInfo)) { return; } final String[] paths = StringUtils.split(pathInfo.startsWith("/") ? pathInfo.substring(1) : pathInfo, '/'); final ServletService service = getService(paths); if (service == null) { logger.debug("Unknown/unhandled route: {}", pathInfo); } else { service.handleGet(req, paths, resp); } } /** * Handles the post by routing it to the appropriate {@link ServletService} or logging it if no route found */ @Override protected void doPost(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws ServletException, IOException { Objects.requireNonNull(req, "req cannot be null"); Objects.requireNonNull(resp, "resp cannot be null"); if (logger.isDebugEnabled()) { req.getReader().mark(150000); logger.debug("doPost: {} with {}", getFullURL(req), IOUtils.toString(req.getReader())); req.getReader().reset(); } final String pathInfo = NeeoUtil.decodeURIComponent(req.getPathInfo()); final String[] paths = StringUtils.split(pathInfo.startsWith("/") ? pathInfo.substring(1) : pathInfo, '/'); final ServletService service = getService(paths); if (service == null) { logger.debug("Unknown/unhandled route: {}", pathInfo); } else { service.handlePost(req, paths, resp); } } /** * Gets the {@link ServletService} for the given path. * * @param paths the non-null, non-empty paths * @return the service that can handle the path or null if none can */ @Nullable protected ServletService getService(String[] paths) { Objects.requireNonNull(paths, "paths cannot be null"); if (paths.length == 0) { throw new IllegalArgumentException("paths cannot be of 0 length"); } for (ServletService service : services) { if (service.canHandleRoute(paths)) { return service; } } return null; } /** * Helper method to get the full URL from the given request * * @param request the non-null request * @return the full URL */ protected static String getFullURL(HttpServletRequest request) { Objects.requireNonNull(request, "request cannot be null"); StringBuffer requestURL = request.getRequestURL(); String queryString = request.getQueryString(); if (queryString == null) { return NeeoUtil.decodeURIComponent(requestURL.toString()); } else { return NeeoUtil.decodeURIComponent(requestURL.append('?').append(queryString).toString()); } } /** * Called to have the servlet receive and handle an event (from {@link NeeoService}). The servlet will simply * delegate the event to each {@link ServletService} until one of the services handles the event * * @param event the non-null event */ public void receive(Event event) { Objects.requireNonNull(event, "event cannot be null"); for (ServletService target : services) { if (target.handleEvent(event)) { break; } } } /** * Returns all the {@link EventFilter} (s) to use to filter events * * @return the possibly null event filters; */ @Nullable public List<EventFilter> getEventFilters() { return eventFilters; } /** * Simply closes the API and each of the services */ @Override public void close() { for (ServletService service : services) { NeeoUtil.close(service); } } }