All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.plugin.copytable.web.SingleThreadTableProcessorHandler Maven / Gradle / Ivy

There is a newer version: 0.0.126
Show newest version
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 io.datarouter.plugin.copytable.web;

import static j2html.TagCreator.body;
import static j2html.TagCreator.br;
import static j2html.TagCreator.div;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.p;

import java.util.List;

import javax.inject.Inject;

import io.datarouter.inject.DatarouterInjector;
import io.datarouter.model.databean.Databean;
import io.datarouter.model.key.primary.PrimaryKey;
import io.datarouter.nodewatch.service.TableSamplerService;
import io.datarouter.plugin.copytable.config.DatarouterCopyTablePaths;
import io.datarouter.plugin.copytable.tableprocessor.TableProcessor;
import io.datarouter.plugin.copytable.tableprocessor.TableProcessorRegistry;
import io.datarouter.plugin.copytable.tableprocessor.TableProcessorService;
import io.datarouter.plugin.copytable.tableprocessor.TableProcessorService.TableProcessorSpanResult;
import io.datarouter.scanner.Scanner;
import io.datarouter.util.number.NumberFormatter;
import io.datarouter.util.string.StringTool;
import io.datarouter.web.email.DatarouterHtmlEmailService;
import io.datarouter.web.email.StandardDatarouterEmailHeaderService;
import io.datarouter.web.handler.BaseHandler;
import io.datarouter.web.handler.mav.Mav;
import io.datarouter.web.handler.types.Param;
import io.datarouter.web.handler.types.optional.OptionalString;
import io.datarouter.web.html.form.HtmlForm;
import io.datarouter.web.html.j2html.bootstrap4.Bootstrap4FormHtml;
import io.datarouter.web.html.j2html.bootstrap4.Bootstrap4PageFactory;
import j2html.tags.ContainerTag;

public class SingleThreadTableProcessorHandler extends BaseHandler{

	private static final String
			P_sourceNodeName = "sourceNodeName",
			P_lastKeyString = "lastKeyString",
			P_processorName = "processorName",
			P_numThreads = "numThreads",
			P_putBatchSize = "putBatchSize",
			P_toEmail = "toEmail",
			P_submitAction = "submitAction";

	private static final int DEFAULT_NUM_THREADS = 4;
	private static final int DEFAULT_BATCH_SIZE = 1_000;

	@Inject
	private TableProcessorService service;
	@Inject
	private DatarouterHtmlEmailService htmlEmailService;
	@Inject
	private TableProcessorRegistry processorRegistry;
	@Inject
	private DatarouterCopyTablePaths paths;
	@Inject
	private Bootstrap4PageFactory pageFactory;
	@Inject
	private CopyTableChangelogRecorderService changelogRecorderService;
	@Inject
	private StandardDatarouterEmailHeaderService standardDatarouterEmailHeaderService;
	@Inject
	private DatarouterInjector injector;
	@Inject
	private TableSamplerService tableSamplerService;

	@Handler(defaultHandler = true)
	private ,
			D extends Databean>
	Mav defaultHandler(
			@Param(P_sourceNodeName) OptionalString sourceName,
			@Param(P_processorName) OptionalString processorName,
			@Param(P_lastKeyString) OptionalString lastKeyString,
			@Param(P_toEmail) OptionalString toEmail,
			@Param(P_numThreads) OptionalString numThreads,
			@Param(P_putBatchSize) OptionalString putBatchSize,
			@Param(P_submitAction) OptionalString submitAction){
		String errorNumThreads = null;
		String errorPutBatchSize = null;

		if(submitAction.isPresent()){
			try{
				if(numThreads.map(StringTool::nullIfEmpty).isPresent()){
					Integer.valueOf(numThreads.get());
				}
			}catch(Exception e){
				errorNumThreads = "Please specify an integer";
			}
			try{
				if(putBatchSize.map(StringTool::nullIfEmpty).isPresent()){
					Integer.valueOf(putBatchSize.get());
				}
			}catch(Exception e){
				errorPutBatchSize = "Please specify an integer";
			}
		}

		List possibleNodes = tableSamplerService.scanAllSortedMapStorageNodes()
				.map(node -> node.getClientId().getName() + "." + node.getFieldInfo().getTableName())
				.append("")
				.sort()
				.list();
		List possibleProcessors = Scanner.of(processorRegistry.getAll())
				.map(Class::getSimpleName)
				.sort()
				.list();
		var form = new HtmlForm()
				.withMethod("post");
		form.addSelectField()
				.withDisplay("Node Name")
				.withName(P_sourceNodeName)
				.withValues(possibleNodes);
		form.addSelectField()
				.withDisplay("Processor Name")
				.withName(P_processorName)
				.withValues(possibleProcessors);
		form.addTextField()
				.withDisplay("Last Key String")
				//add validation
				.withName(P_lastKeyString)
				.withValue(lastKeyString.orElse(null));
		form.addTextField()
				.withDisplay("Num Threads")
				.withError(errorNumThreads)
				.withName(P_numThreads)
				.withPlaceholder(DEFAULT_NUM_THREADS + "")
				.withValue(numThreads.orElse(null));
		form.addTextField()
				.withDisplay("Batch Size")
				.withError(errorPutBatchSize)
				.withName(P_putBatchSize)
				.withPlaceholder(DEFAULT_BATCH_SIZE + "")
				.withValue(putBatchSize.orElse(null));
		form.addTextField()
				.withDisplay("Email on Completion")
				//add validation
				.withName(P_toEmail)
				.withPlaceholder("[email protected]")
				.withValue(toEmail.orElse(null));
		form.addButton()
				.withDisplay("Copy")
				.withValue("anything");

		if(submitAction.isEmpty() || form.hasErrors()){
			return pageFactory.startBuilder(request)
					.withTitle("Table Processor - Single Thread")
					.withContent(Html.makeContent(form))
					.buildMav();
		}

		int actualNumThreads = numThreads
				.map(StringTool::nullIfEmpty)
				.map(Integer::valueOf)
				.orElse(DEFAULT_NUM_THREADS);
		int actualPutBatchSize = putBatchSize
				.map(StringTool::nullIfEmpty)
				.map(Integer::valueOf)
				.orElse(DEFAULT_BATCH_SIZE);
		TableProcessor processor = injector.getInstance(processorRegistry.find(processorName.get())
				.get());
		TableProcessorSpanResult result = service.runTableProcessor(
				sourceName.get(),
				lastKeyString.map(StringTool::nullIfEmpty).orElse(null),
				null,
				processor,
				actualNumThreads,
				actualPutBatchSize,
				1,
				1);
		if(!result.success){
			String message = String.format("The table processor was interrupted unexpectedly with %s."
					+ "  Please resume the processor with lastKey %s",
					result.exception.getMessage(),
					result.resumeFromKeyString);
			return pageFactory.message(request, message);
		}
		var header = standardDatarouterEmailHeaderService.makeStandardHeader();
		String message = String.format("Successfully processed %s records for %s - %s",
				NumberFormatter.addCommas(result.numCopied),
				sourceName.get(),
				processorName.get());
		var body = body(header, p(message));
		if(toEmail.filter(str -> !str.isEmpty()).isPresent()){
			String primaryHref = htmlEmailService.startLinkBuilder()
					.withLocalPath(paths.datarouter.copyTableSingleThread)
					.build();
			var emailBuilder = htmlEmailService.startEmailBuilder()
					.withTitle("Table Processor")
					.withTitleHref(primaryHref)
					.withContent(body)
					.fromAdmin()
					.to(toEmail.get());
			htmlEmailService.trySendJ2Html(emailBuilder);
		}
		changelogRecorderService.recordChangelogForTableProcessor(getSessionInfo(), "Single Thread", sourceName.get(),
				processorName.get());
		return pageFactory.message(request, message);
	}

	private static class Html{

		public static ContainerTag makeContent(HtmlForm htmlForm){
			var form = Bootstrap4FormHtml.render(htmlForm)
					.withClass("card card-body bg-light");
			return div(
					h2("Table Processor - Single Thread"),
					form,
					br())
					.withClass("container mt-3");
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy