package org.opentides.web.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.opentides.bean.BaseEntity;
import org.opentides.bean.ChangeLog;
import org.opentides.bean.SyncEndpoint;
import org.opentides.bean.SyncResults;
import org.opentides.service.ChangeLogService;
import org.opentides.service.SyncEndpointService;
import org.opentides.util.DatabaseUtil;
import org.opentides.web.json.Views;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.gson.Gson;
/**
*
* @author allanctan
*
*/
@Controller
@RequestMapping("/sync")
public class SynchronizableController {
@Autowired
private ChangeLogService changeLogService;
@Autowired
private SyncEndpointService syncEndpointService;
public final String UPDATE_ENDPOINT = "[UPDATE ENDPOINT] ";
public final String WEB_UPDATE = "[WEB_UPDATE] ";
public final String SUCCESS = "[SUCCESS] ";
public final String FAIL = "[FAIL] ";
public final static String EXCEPTION = "[EXCEPTION] ";
@Value("${log.sync.dir}")
private String logDirectory;
@Value("${log.sync.filename}")
private String syncLogFileName;
private static Logger _log = Logger
.getLogger(SynchronizableController.class);
public final static HttpHeaders httpHeaders = initializeAccessAllHeaders();
public String clientCode;
/**
* Returns the list of updates that should be applied from the given
*
* version. The version is the latest version of the endpoint. Returns the
* list of updates after the version upto the most current update.
*
*
* @param name
* @return
*/
@JsonView(Views.FormView.class)
@RequestMapping(value = "/{clientcode}/{branchId}", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity getUpdates(
@PathVariable("clientcode") String clientcode,
@PathVariable("branchId") String branch) {
this.clientCode = clientcode;
getLogger();
SyncResults results = new SyncResults();
SyncEndpoint endpoint = syncEndpointService.findSyncEndpointByClientCode(clientcode);
Long branchId = Long.parseLong(branch);
if (endpoint != null) {
List<ChangeLog> changes = changeLogService
.findAfterVersion(endpoint.getSyncVersion(), branchId);
if (!changes.isEmpty()) {
ChangeLog last = changes.get(changes.size() - 1);
results.setLatestVersion(last.getId());
results.setLogs(changes);
} else {
results.setLatestVersion(endpoint.getSyncVersion());
//results.setLogs(new ArrayList<ChangeLog>());
}
Long targetVersion = this.changeLogService.findTargetVersion(branchId) != null ?
this.changeLogService.findTargetVersion(branchId) : 0L;
results.setTargetVersion(targetVersion);
// Log updates from WEB to POS
_log.info("[" + clientcode + "] " + UPDATE_ENDPOINT
+ " Send Change Log version " + endpoint.getSyncVersion()
+ " to " + results.getLatestVersion() + ". TARGET VERSION: " + targetVersion);
} else {
_log.info("[" + clientcode + "] " + UPDATE_ENDPOINT + FAIL
+ " End point is null");
}
Gson gSon = new Gson();
results.setStatus(SyncResults.Status.SUCCESS);
return new ResponseEntity(gSon.toJson(results), httpHeaders,
HttpStatus.OK);
}
/**
*
*
* Updates the version of the endpoint. Endpoint should invoke this method
* when
*
* @return
*/
@RequestMapping(value = "/success/{clientcode}/{version}", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity updateEndpoint(
@PathVariable("clientcode") String clientcode,
@PathVariable("version") Long version) {
this.clientCode = clientcode;
getLogger();
SyncResults results = new SyncResults();
SyncEndpoint endpoint = syncEndpointService.findSyncEndpointByClientCode(clientcode);
if (endpoint != null) {
endpoint.setSyncVersion(version);
syncEndpointService.save(endpoint);
// Update sync endpoint latest version to WEB
_log.info("[" + clientcode + "] " + WEB_UPDATE + SUCCESS
+ " Previous Endpoint sync success. Web "
+ "will update endpoint sync version to "
+ endpoint.getSyncVersion());
} else {
_log.info("[" + clientcode + "] " + WEB_UPDATE + FAIL
+ " End point is null");
}
results.setLatestVersion(version);
results.setStatus(SyncResults.Status.SUCCESS);
Gson gSon = new Gson();
return new ResponseEntity(gSon.toJson(results), httpHeaders,
HttpStatus.OK);
}
/**
*
*
* Updates the version of the endpoint. Endpoint should invoke this method
* when
*
* @return
*/
@RequestMapping(value = "/upload/{clientcode}/{branchId}", method = {
RequestMethod.GET, RequestMethod.POST }, produces = "application/json")
public ResponseEntity updateServer(
@PathVariable("clientcode") String clientcode,
@PathVariable("branchId") String branch,
HttpServletRequest request) {
this.clientCode = clientcode;
getLogger();
SyncEndpoint endpoint = syncEndpointService.findSyncEndpointByClientCode(clientcode);
Long branchId = Long.parseLong(branch);
StringBuilder appender = new StringBuilder();
String result = SUCCESS;
String errorMsg = "";
SyncResults response = new SyncResults();
String clientParam = "";
try {
clientParam = getBody(request);
appender.append(clientParam);
} catch (IOException e) {
_log.error(EXCEPTION + e.getMessage(), e);
result = FAIL;
errorMsg = "Client parameters parsing error.";
e.printStackTrace();
}
if (endpoint != null) {
List<ChangeLog> changes = changeLogService
.findAfterVersion(endpoint.getSyncVersion(), branchId);
long latestVersion = 0L;
if (changes != null && changes.size() > 0) {
latestVersion = changes.get(changes.size() - 1).getId();
response.setLatestVersion(latestVersion);
}
response.setLogs(changes);
Gson gSon = new Gson();
SyncResults clientChanges = gSon.fromJson(clientParam,
SyncResults.class);
List<ChangeLog> logs = clientChanges.getLogs();
if (logs != null && logs.size() > 0) {
EntityManager em = DatabaseUtil.getEntityManager();
try {
Query query = null;
em.getTransaction().begin();
for (int i = 0; i < logs.size(); i++) {
String sql = logs.get(i).getSqlCommand();
query = em.createNativeQuery(sql);
query.executeUpdate();
int changeLogAction = ChangeLog.INSERT;
if (sql.toLowerCase().contains("update")) {
changeLogAction = ChangeLog.UPDATE;
} else if (sql.toLowerCase().contains("delete")) {
changeLogAction = ChangeLog.DELETE;
}
BaseEntity bse = (BaseEntity) logs.get(i);
ChangeLog cl = new ChangeLog(0L, bse.getClass(),
changeLogAction, "", sql);
latestVersion++;
em.persist(cl);
}
em.getTransaction().commit();
} catch (Exception ex) {
result = FAIL;
errorMsg = "(Error: " + stackTraceToString(ex) + ")";
_log.error(EXCEPTION + ex.getMessage(), ex);
em.getTransaction().rollback();
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
}
response.setLatestVersion(latestVersion);
}else{
result = FAIL;
errorMsg = "End point is null";
}
response.setStatus(SyncResults.Status.SUCCESS);
Gson gSon = new Gson();
_log.info("[" + clientcode + "] " + WEB_UPDATE + result
+ appender.toString() + errorMsg);
return new ResponseEntity(gSon.toJson(response), httpHeaders,
HttpStatus.OK);
}
@SuppressWarnings("deprecation")
public static String getBody(HttpServletRequest request) throws IOException {
String body = null;
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(
inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex)
{
throw ex;
}
}
body = stringBuilder.toString();
} catch (IOException ex) {
_log.error(EXCEPTION + ex.getMessage(), ex);
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = URLDecoder.decode(body);
body = body.replaceAll("clientchanges", "");
body = body.replaceAll("=", "");
return body;
}
public static HttpHeaders initializeAccessAllHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Methods",
"POST, GET, OPTIONS, DELETE");
headers.add("Access-Control-Max-Age", "3600");
headers.add("Access-Control-Allow-Headers", "x-requested-with");
return headers;
}
public void getLogger() {
String logFolderName = logDirectory + clientCode;
String logFilePath = logFolderName + "/" + syncLogFileName;
FileAppender appender = (FileAppender) _log.getAppender("file");
appender.setFile(logFilePath);
appender.setAppend(true);
appender.activateOptions();
}
public String stackTraceToString(Throwable e) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : e.getStackTrace()) {
sb.append(element.toString());
sb.append("\n");
}
return sb.toString();
}
}