Java tutorial
/* ############################################################################### # # # Copyright (C) 2011-2015 OpenMEAP, Inc. # # Credits to Jonathan Schang & Rob Thacher # # # # Released under the LGPLv3 # # # # OpenMEAP is free software: you can redistribute it and/or modify # # it under the terms of the GNU Lesser General Public License as published # # by the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # OpenMEAP is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # You should have received a copy of the GNU Lesser General Public License # # along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. # # # ############################################################################### */ package com.openmeap.services; import static com.openmeap.util.ParameterMapUtils.firstValue; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.Map; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpStatus; import com.openmeap.thirdparty.org.json.me.JSONException; import com.openmeap.thirdparty.org.json.me.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.openmeap.cluster.dto.ClusterNodeRequest; import com.openmeap.constants.FormConstants; import com.openmeap.constants.UrlParamConstants; import com.openmeap.digest.DigestException; import com.openmeap.event.Event; import com.openmeap.event.EventHandler; import com.openmeap.event.EventHandlingException; import com.openmeap.json.JSONObjectBuilder; import com.openmeap.model.InvalidPropertiesException; import com.openmeap.model.ModelManager; import com.openmeap.model.ModelServiceImpl; import com.openmeap.model.dto.ApplicationArchive; import com.openmeap.model.dto.ClusterNode; import com.openmeap.model.dto.GlobalSettings; import com.openmeap.model.event.MapPayloadEvent; import com.openmeap.model.event.ModelEntityEventAction; import com.openmeap.model.event.handler.ArchiveFileDeleteHandler; import com.openmeap.model.event.handler.ArchiveFileUploadHandler; import com.openmeap.model.event.handler.ModelServiceRefreshHandler; import com.openmeap.services.dto.Result; import com.openmeap.util.AuthTokenProvider; import com.openmeap.util.GenericRuntimeException; import com.openmeap.util.ParameterMapUtils; import com.openmeap.util.ServletUtils; import com.openmeap.util.Utils; /** * Used to notify that model items have been modified in the administrative interface. * * Because the admin server is separated from the web-services, * we needed to create a means of notifying the web-services that * items in the persistence context were stale. * * @author schang */ public class ServiceManagementServlet extends HttpServlet { private static final long serialVersionUID = 3528435164985362736L; private Logger logger = LoggerFactory.getLogger(ServiceManagementServlet.class); private ModelManager modelManager = null; private ModelServiceRefreshHandler modelServiceRefreshHandler = null; private ArchiveFileUploadHandler archiveUploadHandler = null; private ArchiveFileDeleteHandler archiveDeleteHandler = null; private WebApplicationContext context = null; public void init() { context = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); modelManager = (ModelManager) context.getBean("modelManager"); modelServiceRefreshHandler = (ModelServiceRefreshHandler) context.getBean("modelServiceRefreshHandler"); archiveUploadHandler = (ArchiveFileUploadHandler) context.getBean("archiveUploadHandler"); archiveDeleteHandler = (ArchiveFileDeleteHandler) context.getBean("archiveDeleteHandler"); } @Override public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { Result result = null; PrintWriter os = new PrintWriter(response.getOutputStream()); logger.debug("Request uri: {}", request.getRequestURI()); logger.debug("Request url: {}", request.getRequestURL()); logger.debug("Query string: {}", request.getQueryString()); if (logger.isDebugEnabled()) { logger.debug("Parameter map: {}", ParameterMapUtils.toString(request.getParameterMap())); } String action = request.getParameter(UrlParamConstants.ACTION); if (action == null) { action = ""; } if (!authenticates(request)) { logger.error("Request failed to authenticate ", request); result = new Result(Result.Status.FAILURE, "Authentication failed"); sendResult(response, os, result); return; } if (action.equals(ModelEntityEventAction.MODEL_REFRESH.getActionName())) { logger.trace("Processing refresh"); result = refresh(request, response); sendResult(response, os, result); return; } else if (action.equals(ClusterNodeRequest.HEALTH_CHECK)) { logger.trace("Cluster node health check"); result = healthCheck(request, response); sendResult(response, os, result); return; } GlobalSettings settings = modelManager.getGlobalSettings(); ClusterNode clusterNode = modelManager.getClusterNode(); if (clusterNode == null) { throw new RuntimeException( "openmeap-services-web needs to be configured as a cluster node in the settings of the admin interface."); } Map<Method, String> validationErrors = clusterNode.validate(); if (validationErrors != null) { throw new RuntimeException(new InvalidPropertiesException(clusterNode, validationErrors)); } if (request.getParameter("clearPersistenceContext") != null && context instanceof AbstractApplicationContext) { logger.trace("Clearing persistence context"); clearPersistenceContext(); } else if (action.equals(ModelEntityEventAction.ARCHIVE_UPLOAD.getActionName())) { logger.trace("Processing archive upload - max file size: {}, storage path prefix: {}", settings.getMaxFileUploadSize(), clusterNode.getFileSystemStoragePathPrefix()); archiveUploadHandler.setFileSystemStoragePathPrefix(clusterNode.getFileSystemStoragePathPrefix()); Map<Object, Object> paramMap = ServletUtils.cloneParameterMap(settings.getMaxFileUploadSize(), clusterNode.getFileSystemStoragePathPrefix(), request); result = handleArchiveEvent(archiveUploadHandler, new MapPayloadEvent(paramMap), paramMap); } else if (action.equals(ModelEntityEventAction.ARCHIVE_DELETE.getActionName())) { logger.trace("Processing archive delete - max file size: {}, storage path prefix: {}", settings.getMaxFileUploadSize(), clusterNode.getFileSystemStoragePathPrefix()); archiveDeleteHandler.setFileSystemStoragePathPrefix(clusterNode.getFileSystemStoragePathPrefix()); Map<Object, Object> paramMap = ServletUtils.cloneParameterMap(settings.getMaxFileUploadSize(), clusterNode.getFileSystemStoragePathPrefix(), request); result = handleArchiveEvent(archiveDeleteHandler, new MapPayloadEvent(paramMap), paramMap); } sendResult(response, os, result); } private void sendResult(HttpServletResponse response, PrintWriter os, Result result) throws IOException { try { if (result.getStatus() != Result.Status.SUCCESS) { response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); } JSONObject jsonResult = new JSONObjectBuilder().toJSON(result); String stringResult = jsonResult.toString(3); logger.debug("returning json result: {}", stringResult); os.print(jsonResult); } catch (JSONException jse) { throw new IOException(jse); } os.flush(); os.close(); } private void clearPersistenceContext() { logger.info("Clearing the persistence context"); ModelServiceImpl ms = (ModelServiceImpl) ((AbstractApplicationContext) context).getBean("modelService"); ms.clearPersistenceContext(); } @SuppressWarnings(value = { "rawtypes", "unchecked" }) private Result handleArchiveEvent(EventHandler eventHandler, Event event, Map<Object, Object> paramMap) throws IOException { String hash = firstValue(UrlParamConstants.APPARCH_HASH, paramMap); String hashType = firstValue(UrlParamConstants.APPARCH_HASH_ALG, paramMap); logger.info("Received request archive upload notification {}:{}", hashType, hash); Result result = null; if (hash != null && hashType != null) { ApplicationArchive arch = new ApplicationArchive(); arch.setHash(hash); arch.setHashAlgorithm(hashType); try { paramMap.put("archive", arch); eventHandler.handle(event); result = new Result(Result.Status.SUCCESS); } catch (EventHandlingException che) { String msg = "Exception occurred handing the ArchiveUploadEvent"; logger.error(msg, che); result = new Result(Result.Status.FAILURE, msg); } } else { String msg = "Either the hash(" + hash + ") or the hashType(" + hashType + ") was null. Both are needed to process an archive event"; logger.error(msg); result = new Result(Result.Status.FAILURE, msg); } return result; } /** * Handles the notification that this node should refresh some object from the database. * * @param os * @param request * @param response * @throws IOException */ private Result refresh(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/javascript"); String refreshType = (String) request.getParameter("type"); String objectId = (String) request.getParameter("id"); Result result = null; if (refreshType != null && objectId != null) { logger.info("Received request to refresh {} with id {}", refreshType, objectId); try { modelServiceRefreshHandler.handleRefresh(refreshType, objectId); logger.info("Refresh for {} with id {} was successful", refreshType, objectId); result = new Result(Result.Status.SUCCESS); } catch (Exception e) { String msg = "Exception occurred refreshing " + refreshType + " with object id " + objectId; logger.error(msg, e); result = new Result(Result.Status.FAILURE, msg); } } else { String msg = "Must specify refresh target class, object primary key, and authentication token."; logger.error(msg, request); result = new Result(Result.Status.FAILURE, msg); } return result; } /** * * @param request * @param response * @return * @throws IOException */ private Result healthCheck(HttpServletRequest request, HttpServletResponse response) throws IOException { String json = Utils.readInputStream(request.getInputStream(), FormConstants.CHAR_ENC_DEFAULT); Result result = null; try { ClusterNodeRequest nodeRequest = (ClusterNodeRequest) new JSONObjectBuilder() .fromJSON(new JSONObject(json), new ClusterNodeRequest()); Map<String, String> properties = (Map<String, String>) context .getBean("openmeapServicesWebPropertiesMap"); synchronized (properties) { properties.put("clusterNodeUrlPrefix", nodeRequest.getClusterNode().getServiceWebUrlPrefix()); properties.put("fileSystemStoragePathPrefix", nodeRequest.getClusterNode().getFileSystemStoragePathPrefix()); } result = new Result(Result.Status.SUCCESS); } catch (JSONException e) { result = new Result(); result.setStatus(Result.Status.FAILURE); String msg = "Failed to parse health status check JSON - " + json; logger.error(msg); result.setMessage(msg); } return result; } /** * Validates that the auth in a request passes validation * @param arg0 * @return */ private Boolean authenticates(HttpServletRequest arg0) { String authSalt = getAuthSalt(); String auth = (String) arg0.getParameter(UrlParamConstants.AUTH_TOKEN); Boolean isGood; try { isGood = AuthTokenProvider.validateAuthToken(authSalt, auth); } catch (DigestException e) { throw new GenericRuntimeException(e); } logger.debug("Authentication of token \"{}\" with salt \"{}\" returned {}", new Object[] { authSalt, auth, isGood }); return (auth != null && isGood); } // ACCESSORS public String getAuthSalt() { GlobalSettings settings = modelManager.getGlobalSettings(); return settings.getServiceManagementAuthSalt(); } public void setModelManager(ModelManager manager) { modelManager = manager; } public ModelManager getModelManager() { return modelManager; } public void setModelServiceRefreshHandler(ModelServiceRefreshHandler refreshHandler) { modelServiceRefreshHandler = refreshHandler; } public ModelServiceRefreshHandler getModelServiceRefreshHandler() { return modelServiceRefreshHandler; } }