/** * Licensed to JumpMind Inc under one or more contributor * license agreements. See the NOTICE file distributed * with this work for additional information regarding * copyright ownership. JumpMind Inc licenses this file * to you under the GNU General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jumpmind.symmetric.web; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.zip.GZIPInputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.jumpmind.symmetric.ISymmetricEngine; import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.io.data.CsvConstants; import org.jumpmind.symmetric.io.data.CsvUtils; import org.jumpmind.symmetric.io.stage.IStagedResource; import org.jumpmind.symmetric.io.stage.IStagingManager; import org.jumpmind.symmetric.model.IncomingBatch; import org.jumpmind.symmetric.model.Node; import org.jumpmind.symmetric.model.ProcessInfo; import org.jumpmind.symmetric.model.ProcessInfoKey; import org.jumpmind.symmetric.model.ProcessType; import org.jumpmind.symmetric.service.IDataLoaderService; import org.jumpmind.symmetric.service.INodeService; import org.jumpmind.symmetric.service.impl.DataLoaderService; import org.jumpmind.symmetric.service.impl.DataLoaderService.DataLoaderWorker; import org.jumpmind.symmetric.statistic.IStatisticManager; /** * Handles data pushes from nodes. */ public class PushUriHandler extends AbstractUriHandler { ISymmetricEngine engine; public PushUriHandler(ISymmetricEngine engine, IInterceptor... interceptors) { super("/push/*", engine.getParameterService(), interceptors); this.engine = engine; } public void handle(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { String nodeId = ServletUtils.getParameter(req, WebConstants.NODE_ID); String channelId = getChannelId(req); log.info("About to service push request for {}", nodeId); IStagingManager stagingManager = engine.getStagingManager(); IDataLoaderService dataLoaderService = engine.getDataLoaderService(); INodeService nodeService = engine.getNodeService(); IStatisticManager statisticManager = engine.getStatisticManager(); String identityNodeId = nodeService.findIdentityNodeId(); ProcessInfo processInfo = statisticManager .newProcessInfo(new ProcessInfoKey(nodeId, identityNodeId, ProcessType.TRANSFER_FROM, channelId)); BufferedReader reader = null; BufferedWriter writer = null; DataLoaderWorker worker = null; try { Node sourceNode = engine.getNodeService().findNode(nodeId); processInfo.setStatus(ProcessInfo.Status.TRANSFERRING); reader = new BufferedReader(new InputStreamReader(createInputStream(req))); long streamToFileThreshold = parameterService.getLong(ParameterConstants.STREAM_TO_FILE_THRESHOLD); String line = reader.readLine(); StringBuilder batchPrefix = new StringBuilder(); Long batchId = null; while (line != null) { if (line.startsWith(CsvConstants.BATCH)) { batchId = getBatchId(line); IStagedResource resource = stagingManager.create(streamToFileThreshold, Constants.STAGING_CATEGORY_INCOMING, nodeId, batchId); writer = resource.getWriter(); writer.write(batchPrefix.toString()); } else if (line.startsWith(CsvConstants.COMMIT)) { writer.write(line); writer.close(); writer = null; if (worker == null) { worker = dataLoaderService.createDataLoaderWorker(ProcessType.LOAD_FROM_PUSH, channelId, sourceNode); } worker.queueUpLoad(new IncomingBatch(batchId, nodeId)); batchId = null; } if (batchId == null) { batchPrefix.append(line).append("\n"); } else if (writer != null) { writer.write(line); writer.write("\n"); } line = reader.readLine(); } processInfo.setStatus(ProcessInfo.Status.OK); } catch (RuntimeException ex) { processInfo.setStatus(ProcessInfo.Status.ERROR); throw ex; } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(writer); } PrintWriter resWriter = res.getWriter(); if (worker != null) { worker.queueUpLoad(new DataLoaderService.EOM()); while (!worker.isComplete()) { String status = "done"; IncomingBatch batch = worker.waitForNextBatchToComplete(); if (batch == null) { status = "in progress"; batch = worker.getCurrentlyLoading(); } if (batch != null && !(batch instanceof DataLoaderService.EOM)) { ArrayList<IncomingBatch> list = new ArrayList<IncomingBatch>(1); list.add(batch); log.info("sending {} ack ... for {}", status, batch); // TODO 13 support resWriter.write(engine.getTransportManager().getAcknowledgementData(false, identityNodeId, list)); resWriter.write("\n"); resWriter.flush(); } } } res.flushBuffer(); log.debug("Done servicing push request for {}", nodeId); } protected Long getBatchId(String line) { String[] tokens = CsvUtils.tokenizeCsvData(line); if (tokens.length > 1) { return new Long(tokens[1]); } else { return null; } } // protected void push(String sourceNodeId, InputStream inputStream, OutputStream outputStream) throws IOException { // long ts = System.currentTimeMillis(); // try { // Node sourceNode = nodeService.findNode(sourceNodeId); // dataLoaderService.loadDataFromPush(sourceNode, inputStream, outputStream); // } finally { // statisticManager.incrementNodesPushed(1); // statisticManager.incrementTotalNodesPushedTime(System.currentTimeMillis() - ts); // } // } protected InputStream createInputStream(HttpServletRequest req) throws IOException { InputStream is = null; String contentType = req.getHeader("Content-Type"); boolean useCompression = contentType != null && contentType.equalsIgnoreCase("gzip"); is = req.getInputStream(); if (useCompression) { is = new GZIPInputStream(is); } return is; } }