org.jumpmind.symmetric.web.rest.RestService Maven / Gradle / Ivy
The newest version!
/**
* 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
* .
*
* 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.rest;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.MDC;
import org.jumpmind.db.sql.ISqlTemplate;
import org.jumpmind.db.sql.Row;
import org.jumpmind.exception.IoException;
import org.jumpmind.symmetric.ISymmetricEngine;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.io.data.writer.StructureDataWriter.PayloadType;
import org.jumpmind.symmetric.model.BatchAck;
import org.jumpmind.symmetric.model.BatchAckResult;
import org.jumpmind.symmetric.model.IncomingBatch;
import org.jumpmind.symmetric.model.IncomingBatch.Status;
import org.jumpmind.symmetric.model.NetworkedNode;
import org.jumpmind.symmetric.model.NodeChannel;
import org.jumpmind.symmetric.model.NodeHost;
import org.jumpmind.symmetric.model.NodeSecurity;
import org.jumpmind.symmetric.model.OutgoingBatch;
import org.jumpmind.symmetric.model.OutgoingBatchSummary;
import org.jumpmind.symmetric.model.OutgoingBatchWithPayload;
import org.jumpmind.symmetric.model.ProcessInfo;
import org.jumpmind.symmetric.model.ProcessInfoKey;
import org.jumpmind.symmetric.model.ProcessInfoKey.ProcessType;
import org.jumpmind.symmetric.service.IAcknowledgeService;
import org.jumpmind.symmetric.service.IDataExtractorService;
import org.jumpmind.symmetric.service.IDataLoaderService;
import org.jumpmind.symmetric.service.INodeService;
import org.jumpmind.symmetric.service.IOutgoingBatchService;
import org.jumpmind.symmetric.service.IRegistrationService;
import org.jumpmind.symmetric.service.ITriggerRouterService;
import org.jumpmind.symmetric.statistic.IStatisticManager;
import org.jumpmind.symmetric.web.ServerSymmetricEngine;
import org.jumpmind.symmetric.web.SymmetricEngineHolder;
import org.jumpmind.symmetric.web.WebConstants;
import org.jumpmind.symmetric.web.rest.model.Batch;
import org.jumpmind.symmetric.web.rest.model.BatchAckResults;
import org.jumpmind.symmetric.web.rest.model.BatchResult;
import org.jumpmind.symmetric.web.rest.model.BatchResults;
import org.jumpmind.symmetric.web.rest.model.ChannelStatus;
import org.jumpmind.symmetric.web.rest.model.Engine;
import org.jumpmind.symmetric.web.rest.model.EngineList;
import org.jumpmind.symmetric.web.rest.model.Heartbeat;
import org.jumpmind.symmetric.web.rest.model.Node;
import org.jumpmind.symmetric.web.rest.model.NodeList;
import org.jumpmind.symmetric.web.rest.model.NodeStatus;
import org.jumpmind.symmetric.web.rest.model.PullDataResults;
import org.jumpmind.symmetric.web.rest.model.QueryResults;
import org.jumpmind.symmetric.web.rest.model.RegistrationInfo;
import org.jumpmind.symmetric.web.rest.model.RestError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.wordnik.swagger.annotations.ApiOperation;
/**
* This is a REST API for SymmetricDS. The API will be active only if
* rest.api.enable=true. The property is turned off by default. The REST API is
* available at http://hostname:port/api for the stand alone SymmetricDS
* installation.
*
*
* General HTTP Responses to the methods:
*
* -
* ALL Methods may return the following HTTP responses.
*
* In general:
*
* - HTTP 2xx = Success
* - HTTP 4xx = Problem on the caller (client) side
* - HTTP 5xx - Problem on the REST service side
*
* ALL Methods
*
* - HTTP 401 - Unauthorized. You have not successfully authenticated.
* Authentication details are in the response body.
* - HTTP 404 - Not Found. You attempted to perform an operation on a resource
* that doesn't exist. I.E. you tried to start or stop an engine that doesn't
* exist.
* - HTTP 405 - Method Not Allowed. I.E. you attempted a service call that
* uses the default engine (/engine/identity vs engine/{engine}/identity) and
* there was more than one engine found on the server.
* - HTTP 500 - Internal Server Error. Something went wrong on the server /
* service, and we couldn't fulfill the request. Details are in the response
* body.
*
*
* -
* GET Methods
*
* - HTTP 200 - Success with result contained in the response body.
* - HTTP 204 - Success with no results. Your GET request completed
* successfully, but found no matching entities.
*
*
*
*/
@Controller
public class RestService {
protected final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
ServletContext context;
/**
* Provides a list of {@link Engine} that are configured on the node.
*
* @return {@link EngineList} - Engines configured on the node
*
*
* Example xml reponse is as follows:
* {@code
*
*
* RootSugarDB-root
*
*
* }
*
* Example json response is as follows:
* {"engines":[{"name":"RootSugarDB-root"}]}
*
*/
@ApiOperation(value = "Obtain a list of configured Engines")
@RequestMapping(value = "/enginelist", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final EngineList getEngineList() {
EngineList list = new EngineList();
Collection engines = getSymmetricEngineHolder().getEngines()
.values();
for (ISymmetricEngine engine : engines) {
if (engine.getParameterService().is(ParameterConstants.REST_API_ENABLED)) {
list.addEngine(new Engine(engine.getEngineName()));
}
}
return list;
}
/**
* Provides Node information for the single engine
*
* return {@link Node}
*
*
* Example xml reponse is as follows:
* {@code
*
* 0
* 0
* server01
* true
* 2012-12-20T09:26:02-05:00
* server01
* true
* true
* false
* http://machine-name:31415/sync/RootSugarDB-root
*
* }
*
* Example json response is as follows:
* {"name":"server01","externalId":"server01","registrationServer":true,"syncUrl":"http://machine-name:31415/sync/RootSugarDB-root","batchToSendCount":0,"batchInErrorCount":0,"lastHeartbeat":1356013562000,"registered":true,"initialLoaded":true,"reverseInitialLoaded":false}
*
*/
@ApiOperation(value = "Obtain node information for the single engine")
@RequestMapping(value = "engine/node", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final Node getNode() {
return nodeImpl(getSymmetricEngine());
}
/**
* Provides Node information for the specified engine
*/
@ApiOperation(value = "Obtain node information for he specified engine")
@RequestMapping(value = "engine/{engine}/node", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final Node getNode(@PathVariable("engine") String engineName) {
return nodeImpl(getSymmetricEngine(engineName));
}
/**
* Provides a list of children that are registered with this engine.
*
* return {@link Node}
*
*
* Example xml reponse is as follows:
* {@code
*
*
* 0
* 0
* client01
* true
* client01
* true
* false
* false
* http://machine-name:31418/sync/ClientSugarDB-client01
*
*
* }
*
* Example json response is as follows:
* {"nodes":[{"name":"client01","externalId":"client01","registrationServer":false,"syncUrl":"http://gwilmer-laptop:31418/sync/ClientSugarDB-client01","batchToSendCount":0,"batchInErrorCount":0,"lastHeartbeat":null,"registered":true,"initialLoaded":true,"reverseInitialLoaded":false}]}
*
*/
@ApiOperation(value = "Obtain list of children for the single engine")
@RequestMapping(value = "engine/children", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final NodeList getChildren() {
return childrenImpl(getSymmetricEngine());
}
/**
* Provides a list of children {@link Node} that are registered with this
* engine.
*/
@ApiOperation(value = "Obtain list of children for the specified engine")
@RequestMapping(value = "engine/{engine}/children", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final NodeList getChildrenByEngine(@PathVariable("engine") String engineName) {
return childrenImpl(getSymmetricEngine(engineName));
}
/**
* Takes a snapshot for this engine and streams it to the client. The result of this
* call is a stream that should be written to a zip file. The zip contains configuration
* and operational information about the installation and can be used to diagnose
* state of the node
*/
@ApiOperation(value = "Take a diagnostic snapshot for the single engine")
@RequestMapping(value = "engine/snapshot", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final void getSnapshot(HttpServletResponse resp) {
getSnapshot(getSymmetricEngine().getEngineName(), resp);
}
/**
* Executes a select statement on the node and returns results.
*
* Example json response is as follows:
* {"nbrResults":1,"results":[{"rowNum":1,"columnData":[{"ordinal":1,"name":"node_id","value":"root"}]}]}
*
*/
@ApiOperation(value = "Execute the specified SQL statement on the single engine")
@RequestMapping(value = "engine/querynode", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final QueryResults getQueryNode(@RequestParam(value = "query") String sql) {
return queryNodeImpl(getSymmetricEngine(), sql);
}
/**
* Executes a select statement on the node and returns results.
*/
@ApiOperation(value = "Execute the specified SQL statement for the specified engine")
@RequestMapping(value = "engine/{engine}/querynode", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final QueryResults getQueryNode(@PathVariable("engine") String engineName,
@RequestParam(value = "query") String sql) {
return queryNodeImpl(getSymmetricEngine(engineName), sql);
}
/**
* Takes a snapshot for the specified engine and streams it to the client.
*/
@ApiOperation(value = "Take a diagnostic snapshot for the specified engine")
@RequestMapping(value = "engine/{engine}/snapshot", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final void getSnapshot(@PathVariable("engine") String engineName, HttpServletResponse resp) {
BufferedInputStream bis = null;
try {
ISymmetricEngine engine = getSymmetricEngine(engineName);
File file = engine.snapshot();
resp.setHeader("Content-Disposition",
String.format("attachment; filename=%s", file.getName()));
bis = new BufferedInputStream(new FileInputStream(file));
IOUtils.copy(bis, resp.getOutputStream());
} catch (IOException e) {
throw new IoException(e);
} finally {
IOUtils.closeQuietly(bis);
}
}
/**
* Loads a configuration profile for the single engine on the node.
*
* @param file
* A file stream that contains the profile itself.
*/
@ApiOperation(value = "Load a configuration file to the single engine")
@RequestMapping(value = "engine/profile", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postProfile(@RequestParam MultipartFile file) {
loadProfileImpl(getSymmetricEngine(), file);
}
/**
* Loads a configuration profile for the specified engine on the node.
*
* @param file
* A file stream that contains the profile itself.
*/
@ApiOperation(value = "Load a configuration file to the specified engine")
@RequestMapping(value = "engine/{engine}/profile", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postProfileByEngine(@PathVariable("engine") String engineName,
@RequestParam(value = "file") MultipartFile file) {
loadProfileImpl(getSymmetricEngine(engineName), file);
}
/**
* Starts the single engine on the node
*/
@ApiOperation(value = "Start the single engine")
@RequestMapping(value = "engine/start", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postStart() {
startImpl(getSymmetricEngine());
}
/**
* Starts the specified engine on the node
*/
@ApiOperation(value = "Start the specified engine")
@RequestMapping(value = "engine/{engine}/start", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postStartByEngine(@PathVariable("engine") String engineName) {
startImpl(getSymmetricEngine(engineName));
}
/**
* Stops the single engine on the node
*/
@ApiOperation(value = "Stop the single engine")
@RequestMapping(value = "engine/stop", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postStop() {
stopImpl(getSymmetricEngine());
}
/**
* Stops the specified engine on the node
*/
@ApiOperation(value = "Stop the specified engine")
@RequestMapping(value = "engine/{engine}/stop", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postStopByEngine(@PathVariable("engine") String engineName) {
stopImpl(getSymmetricEngine(engineName));
}
/**
* Creates instances of triggers for each entry configured table/trigger for
* the single engine on the node
*/
@ApiOperation(value = "Sync triggers on the single engine")
@RequestMapping(value = "engine/synctriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postSyncTriggers(
@RequestParam(required = false, value = "force") boolean force) {
syncTriggersImpl(getSymmetricEngine(), force);
}
/**
* Creates instances of triggers for each entry configured table/trigger for
* the specified engine on the node
*/
@ApiOperation(value = "Sync triggers on the specified engine")
@RequestMapping(value = "engine/{engine}/synctriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postSyncTriggersByEngine(@PathVariable("engine") String engineName,
@RequestParam(required = false, value = "force") boolean force) {
syncTriggersImpl(getSymmetricEngine(engineName), force);
}
/**
* Removes instances of triggers for each entry configured table/trigger for
* the single engine on the node
*/
@ApiOperation(value = "Drop triggers on the single engine")
@RequestMapping(value = "engine/droptriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postDropTriggers() {
dropTriggersImpl(getSymmetricEngine());
}
/**
* Removes instances of triggers for each entry configured table/trigger for
* the specified engine on the node
*/
@ApiOperation(value = "Drop triggers on the specified engine")
@RequestMapping(value = "engine/{engine}/droptriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postDropTriggersByEngine(@PathVariable("engine") String engineName) {
dropTriggersImpl(getSymmetricEngine(engineName));
}
/**
* Removes instances of triggers for the specified table for the single
* engine on the node
*/
@ApiOperation(value = "Drop triggers for the specified table on the single engine")
@RequestMapping(value = "engine/table/{table}/droptriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postDropTriggersByTable(@PathVariable("table") String tableName) {
dropTriggersImpl(getSymmetricEngine(), tableName);
}
/**
* Removes instances of triggers for the specified table for the single
* engine on the node
*
*/
@ApiOperation(value = "Drop triggers for the specified table on the specified engine")
@RequestMapping(value = "engine/{engine}/table/{table}/droptriggers", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postDropTriggersByEngineByTable(@PathVariable("engine") String engineName,
@PathVariable("table") String tableName) {
dropTriggersImpl(getSymmetricEngine(engineName), tableName);
}
/**
* Uninstalls all SymmetricDS objects from the given node (database) for the
* single engine on the node
*/
@ApiOperation(value = "Uninstall SymmetricDS on the single engine")
@RequestMapping(value = "engine/uninstall", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postUninstall() {
uninstallImpl(getSymmetricEngine());
}
/**
* Uninstalls all SymmetricDS objects from the given node (database) for the
* specified engine on the node
*
*/
@ApiOperation(value = "Uninstall SymmetricDS on the specified engine")
@RequestMapping(value = "engine/{engine}/uninstall", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postUninstallByEngine(@PathVariable("engine") String engineName) {
uninstallImpl(getSymmetricEngine(engineName));
}
/**
* Reinitializes the given node (database) for the single engine on the node
*/
@ApiOperation(value = "Reinitiailize SymmetricDS on the single engine")
@RequestMapping(value = "engine/reinitialize", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postReinitialize() {
reinitializeImpl(getSymmetricEngine());
}
/**
* Reinitializes the given node (database) for the specified engine on the
* node
*
*/
@ApiOperation(value = "Reinitiailize SymmetricDS on the specified engine")
@RequestMapping(value = "engine/{engine}/reinitialize", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postReinitializeByEngine(@PathVariable("engine") String engineName) {
reinitializeImpl(getSymmetricEngine(engineName));
}
/**
* Refreshes cache for the single engine on the node
*/
@ApiOperation(value = "Refresh caches on the single engine")
@RequestMapping(value = "engine/refreshcache", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postClearCaches() {
clearCacheImpl(getSymmetricEngine());
}
/**
* Refreshes cache for the specified engine on the node node
*
*/
@ApiOperation(value = "Refresh caches on the specified engine")
@RequestMapping(value = "engine/{engine}/refreshcache", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postClearCachesByEngine(@PathVariable("engine") String engineName) {
clearCacheImpl(getSymmetricEngine(engineName));
}
/**
* Returns an overall status for the single engine of the node.
*
* @return {@link NodeStatus}
*
*
* Example xml reponse is as follows:
* {@code
*
* 0
* 0
* Microsoft SQL Server
* 9.0
* professional
* root
* true
* 2012-11-17 14:52:19.267
* RootSugarDB
* root
* true
* false
* true
* 3.1.10
* true
* http://my-machine-name:31415/sync/RootSugarDB-root
*
* }
*
* Example json response is as follows:
* {"started":true,"registered":true,"registrationServer":false,"initialLoaded":true,
* "nodeId":"root","nodeGroupId":"RootSugarDB","externalId":"root",
* "syncUrl":"http://my-machine-name:31415/sync/RootSugarDB-root","databaseType":"Microsoft SQL Server",
* "databaseVersion":"9.0","syncEnabled":true,"createdAtNodeId":null,"batchToSendCount":0,
* "batchInErrorCount":0,"deploymentType":"professional","symmetricVersion":"3.1.10",
* "lastHeartbeat":"2012-11-17 15:15:00.033","hearbeatInterval":null}
*
*/
@ApiOperation(value = "Obtain the status of the single engine")
@RequestMapping(value = "/engine/status", method = RequestMethod.GET)
@ResponseBody
public final NodeStatus getStatus() {
return nodeStatusImpl(getSymmetricEngine());
}
/**
* Returns an overall status for the specified engine of the node.
*
* @return {@link NodeStatus}
*/
@ApiOperation(value = "Obtain the status of the specified engine")
@RequestMapping(value = "/engine/{engine}/status", method = RequestMethod.GET)
@ResponseBody
public final NodeStatus getStatusByEngine(@PathVariable("engine") String engineName) {
return nodeStatusImpl(getSymmetricEngine(engineName));
}
/**
* Returns status of each channel for the single engine of the node.
*
* @return Set<{@link ChannelStatus}>
*/
@ApiOperation(value = "Obtain the channel status of the single engine")
@RequestMapping(value = "/engine/channelstatus", method = RequestMethod.GET)
@ResponseBody
public final Set getChannelStatus(@PathVariable("engine") String engineName) {
return channelStatusImpl(getSymmetricEngine());
}
/**
* Returns status of each channel for the specified engine of the node.
*
* @return Set<{@link ChannelStatus}>
*/
@ApiOperation(value = "Obtain the channel status of the specified engine")
@RequestMapping(value = "/engine/{engine}/channelstatus", method = RequestMethod.GET)
@ResponseBody
public final Set getChannelStatusByEngine(
@PathVariable("engine") String engineName) {
return channelStatusImpl(getSymmetricEngine(engineName));
}
/**
* Removes (unregisters and cleans up) a node for the single engine
*/
@ApiOperation(value = "Remove specified node (unregister and clean up) for the single engine")
@RequestMapping(value = "/engine/removenode", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postRemoveNode(@RequestParam(value = "nodeId") String nodeId) {
postRemoveNodeByEngine(nodeId, getSymmetricEngine().getEngineName());
}
/**
* Removes (unregisters and cleans up) a node for the single engine
*/
@ApiOperation(value = "Remove specified node (unregister and clean up) for the specified engine")
@RequestMapping(value = "/engine/{engine}/removenode", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@ResponseBody
public final void postRemoveNodeByEngine(@RequestParam(value = "nodeId") String nodeId,
@PathVariable("engine") String engineName) {
getSymmetricEngine(engineName).removeAndCleanupNode(nodeId);
}
/**
* Requests the server to add this node to the synchronization scenario as a
* "pull only" node
*
* @param externalId
* The external id for this node
* @param nodeGroup
* The node group to which this node belongs
* @param databaseType
* The database type for this node
* @param databaseVersion
* The database version for this node
* @param hostName
* The host name of the machine on which the client is running
* @return {@link RegistrationInfo}
*
*
* Example json response is as follows:
* {"registered":false,"nodeId":null,"syncUrl":null,"nodePassword":null}
* In the above example, the node attempted to register, but was not able to successfully register
* because registration was not open on the server. Checking the "registered" element will allow you
* to determine whether the node was successfully registered.
* The following example shows the results from the registration after registration has been opened
* on the server for the given node.
* {"registered":true,"nodeId":"001","syncUrl":"http://myserverhost:31415/sync/server-000","nodePassword":"1880fbffd2bc2d00e1d58bd0c734ff"}
* The nodeId, syncUrl and nodePassword should be stored for subsequent calls to the REST API.
*
*/
@ApiOperation(value = "Register the specified node for the single engine")
@RequestMapping(value = "/engine/registernode", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final RegistrationInfo postRegisterNode(
@RequestParam(value = "externalId") String externalId,
@RequestParam(value = "nodeGroupId") String nodeGroupId,
@RequestParam(value = "databaseType") String databaseType,
@RequestParam(value = "databaseVersion") String databaseVersion,
@RequestParam(value = "hostName") String hostName) {
return postRegisterNode(getSymmetricEngine().getEngineName(), externalId, nodeGroupId,
databaseType, databaseVersion, hostName);
}
@ApiOperation(value = "Register the specified node for the specified engine")
@RequestMapping(value = "/engine/{engine}/registernode", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final RegistrationInfo postRegisterNode(@PathVariable("engine") String engineName,
@RequestParam(value = "externalId") String externalId,
@RequestParam(value = "nodeGroupId") String nodeGroupId,
@RequestParam(value = "databaseType") String databaseType,
@RequestParam(value = "databaseVersion") String databaseVersion,
@RequestParam(value = "hostName") String hostName) {
ISymmetricEngine engine = getSymmetricEngine(engineName);
IRegistrationService registrationService = engine.getRegistrationService();
INodeService nodeService = engine.getNodeService();
RegistrationInfo regInfo = new org.jumpmind.symmetric.web.rest.model.RegistrationInfo();
try {
org.jumpmind.symmetric.model.Node processedNode = registrationService
.registerPullOnlyNode(externalId, nodeGroupId, databaseType, databaseVersion);
regInfo.setRegistered(processedNode.isSyncEnabled());
if (regInfo.isRegistered()) {
regInfo.setNodeId(processedNode.getNodeId());
NodeSecurity nodeSecurity = nodeService.findNodeSecurity(processedNode.getNodeId());
regInfo.setNodePassword(nodeSecurity.getNodePassword());
org.jumpmind.symmetric.model.Node modelNode = nodeService.findIdentity();
regInfo.setSyncUrl(modelNode.getSyncUrl());
//do an initial heartbeat
Heartbeat heartbeat = new Heartbeat();
heartbeat.setNodeId(regInfo.getNodeId());
heartbeat.setHostName(hostName);
Date now = new Date();
heartbeat.setCreateTime(now);
heartbeat.setLastRestartTime(now);
heartbeat.setHeartbeatTime(now);
this.heartbeatImpl(engine, heartbeat);
}
// TODO: Catch a RegistrationRedirectException and redirect.
} catch (IOException e) {
throw new IoException(e);
}
return regInfo;
}
/**
* Pulls pending batches (data) for a given node.
* @param nodeId The node id of the node requesting to pull data
* @param securityToken The security token or password used to authenticate the pull. The security token is provided during the registration process.
* @param useJdbcTimestampFormat
* @param useUpsertStatements
* @param useDelimitedIdentifiers
* @param hostName The name of the host machine requesting the pull. Only required if you have the rest heartbeat on pull paramter set.
* @return {@link PullDataResults}
*
* Example json response is as follows:
* {"nbrBatches":2,"batches":[{"batchId":20,"sqlStatements":["insert into table1 (field1, field2) values (value1,value2);","update table1 set field1=value1;"]},{"batchId":21,"sqlStatements":["insert into table2 (field1, field2) values (value1,value2);","update table2 set field1=value1;"]}]}
*
* If there are no batches to be pulled, the json response will look as follows:
* {"nbrBatches":0,"batches":[]}
*
*/
@ApiOperation(value = "Pull pending batches for the specified node for the single engine")
@RequestMapping(value = "/engine/pulldata", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final PullDataResults getPullData(
@RequestParam(value = WebConstants.NODE_ID) String nodeId,
@RequestParam(value = WebConstants.SECURITY_TOKEN) String securityToken,
@RequestParam(value = "useJdbcTimestampFormat", required=false, defaultValue="true")
boolean useJdbcTimestampFormat,
@RequestParam(value = "useUpsertStatements", required=false, defaultValue="false")
boolean useUpsertStatements,
@RequestParam(value = "useDelimitedIdentifiers", required=false, defaultValue="true")
boolean useDelimitedIdentifiers,
@RequestParam(value = "hostName", required=false) String hostName) {
return getPullData(getSymmetricEngine().getEngineName(), nodeId, securityToken,
useJdbcTimestampFormat, useUpsertStatements, useDelimitedIdentifiers, hostName);
}
@ApiOperation(value = "Pull pending batches for the specified node for the specified engine")
@RequestMapping(value = "/engine/{engine}/pulldata", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public final PullDataResults getPullData(@PathVariable("engine") String engineName,
@RequestParam(value = WebConstants.NODE_ID) String nodeId,
@RequestParam(value = WebConstants.SECURITY_TOKEN) String securityToken,
@RequestParam(value = "useJdbcTimestampFormat", required=false, defaultValue="true")
boolean useJdbcTimestampFormat,
@RequestParam(value = "useUpsertStatements", required=false, defaultValue="false")
boolean useUpsertStatements,
@RequestParam(value = "useDelimitedIdentifiers", required=false, defaultValue="true")
boolean useDelimitedIdentifiers,
@RequestParam(value = "hostName", required=false) String hostName) {
ISymmetricEngine engine = getSymmetricEngine(engineName);
IDataExtractorService dataExtractorService = engine.getDataExtractorService();
IStatisticManager statisticManager = engine.getStatisticManager();
INodeService nodeService = engine.getNodeService();
org.jumpmind.symmetric.model.Node targetNode = nodeService
.findNode(nodeId);
if (securityVerified(nodeId, engine, securityToken)) {
ProcessInfo processInfo = statisticManager.newProcessInfo(new ProcessInfoKey(
nodeService.findIdentityNodeId(), nodeId, ProcessType.REST_PULL_HANLDER));
try {
PullDataResults results = new PullDataResults();
List