diff --git a/cassiopeia/pom.xml b/cassiopeia/pom.xml index 754a27d..1fa7031 100644 --- a/cassiopeia/pom.xml +++ b/cassiopeia/pom.xml @@ -12,8 +12,8 @@ 1.7.5 4.3.1.RELEASE 4.1.3.RELEASE - 0.2.1 - 0.4.1 + 0.4 + 0.4.2 cassiopeia diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IKafkaRequestSender.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IKafkaRequestSender.java new file mode 100644 index 0000000..bb86952 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IKafkaRequestSender.java @@ -0,0 +1,10 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.RequestInfo; + +public interface IKafkaRequestSender { + + public abstract void sendRequest(String requestId, String documentId, + RequestInfo info); + +} \ No newline at end of file diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestManager.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestManager.java new file mode 100644 index 0000000..fc71de8 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestManager.java @@ -0,0 +1,14 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service; + +import java.util.concurrent.ExecutionException; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.ResendingResult; + +public interface IRequestManager { + + public abstract void startResendingRequests(); + + public abstract ResendingResult getResendingResults() throws InterruptedException, + ExecutionException; + +} \ No newline at end of file diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestResender.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestResender.java new file mode 100644 index 0000000..da644a1 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/IRequestResender.java @@ -0,0 +1,11 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service; + +import java.util.concurrent.Future; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.ResendingResult; + +public interface IRequestResender { + + public Future resendRequests(); + +} \ No newline at end of file diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/KafkaRequestSender.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/KafkaRequestSender.java new file mode 100644 index 0000000..6263e89 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/KafkaRequestSender.java @@ -0,0 +1,84 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl; + +import java.time.OffsetDateTime; +import java.time.ZoneId; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.properties.Properties; +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IKafkaRequestSender; +import edu.asu.diging.gilesecosystem.cassiopeia.rest.DownloadFileController; +import edu.asu.diging.gilesecosystem.requests.ICompletedOCRRequest; +import edu.asu.diging.gilesecosystem.requests.IRequestFactory; +import edu.asu.diging.gilesecosystem.requests.RequestStatus; +import edu.asu.diging.gilesecosystem.requests.exceptions.MessageCreationException; +import edu.asu.diging.gilesecosystem.requests.impl.CompletedOCRRequest; +import edu.asu.diging.gilesecosystem.requests.kafka.IRequestProducer; +import edu.asu.diging.gilesecosystem.util.properties.IPropertiesManager; + +@Service +public class KafkaRequestSender implements IKafkaRequestSender { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private IPropertiesManager propertyManager; + + @Autowired + private IRequestFactory requestFactory; + + @Autowired + private IRequestProducer requestProducer; + + + @PostConstruct + public void init() { + requestFactory.config(CompletedOCRRequest.class); + } + + /* (non-Javadoc) + * @see edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.IKafkaRequestSender#sendRequest(java.lang.String, java.lang.String, java.lang.String, java.lang.String, edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.RequestInfo) + */ + @Override + public void sendRequest(String requestId, String documentId, RequestInfo info) { + String restEndpoint = propertyManager.getProperty(Properties.BASE_URL); + if (restEndpoint.endsWith("/")) { + restEndpoint = restEndpoint.substring(0, restEndpoint.length()-1); + } + + String fileEndpoint = restEndpoint + DownloadFileController.GET_FILE_URL + .replace(DownloadFileController.REQUEST_ID_PLACEHOLDER, requestId) + .replace(DownloadFileController.DOCUMENT_ID_PLACEHOLDER, documentId) + .replace(DownloadFileController.FILENAME_PLACEHOLDER, info.getFilename()); + + ICompletedOCRRequest completedRequest = null; + try { + completedRequest = requestFactory.createRequest(requestId, info.getUploadId()); + } catch (InstantiationException | IllegalAccessException e) { + logger.error("Could not create request.", e); + // this should never happen if used correctly + } + + completedRequest.setDocumentId(documentId); + completedRequest.setDownloadPath(info.getPath()); + completedRequest.setSize(info.getSize()); + completedRequest.setDownloadUrl(fileEndpoint); + completedRequest.setFilename(info.getImageFilename()); + completedRequest.setFileId(info.getFileId()); + completedRequest.setStatus(RequestStatus.COMPLETE); + completedRequest.setOcrDate(OffsetDateTime.now(ZoneId.of("UTC")).toString()); + completedRequest.setTextFilename(info.getFilename()); + + try { + requestProducer.sendRequest(completedRequest, propertyManager.getProperty(Properties.KAFKA_TOPIC_OCR_COMPLETE)); + } catch (MessageCreationException e) { + logger.error("Could not send message.", e); + } + } + +} diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/OCRManager.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/OCRManager.java index c08085d..b119749 100644 --- a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/OCRManager.java +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/OCRManager.java @@ -6,12 +6,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.time.OffsetDateTime; -import java.time.ZoneId; import java.util.Arrays; -import javax.annotation.PostConstruct; - import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.ParseContext; @@ -34,15 +30,9 @@ import org.xml.sax.SAXException; import edu.asu.diging.gilesecosystem.cassiopeia.core.properties.Properties; +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IKafkaRequestSender; import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IOCRManager; -import edu.asu.diging.gilesecosystem.cassiopeia.rest.DownloadFileController; -import edu.asu.diging.gilesecosystem.requests.ICompletedOCRRequest; import edu.asu.diging.gilesecosystem.requests.IOCRRequest; -import edu.asu.diging.gilesecosystem.requests.IRequestFactory; -import edu.asu.diging.gilesecosystem.requests.RequestStatus; -import edu.asu.diging.gilesecosystem.requests.exceptions.MessageCreationException; -import edu.asu.diging.gilesecosystem.requests.impl.CompletedOCRRequest; -import edu.asu.diging.gilesecosystem.requests.kafka.IRequestProducer; import edu.asu.diging.gilesecosystem.util.files.IFileStorageManager; import edu.asu.diging.gilesecosystem.util.properties.IPropertiesManager; @@ -59,16 +49,8 @@ public class OCRManager implements IOCRManager { private IPropertiesManager propertyManager; @Autowired - private IRequestFactory requestFactory; - - @Autowired - private IRequestProducer requestProducer; - - - @PostConstruct - public void init() { - requestFactory.config(CompletedOCRRequest.class); - } + private IKafkaRequestSender kafkaRequestSender; + /* (non-Javadoc) * @see edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.IOCRManager#processOCRRequest(edu.asu.diging.gilesecosystem.requests.IOCRRequest) @@ -76,7 +58,7 @@ public void init() { @Override public void processOCRRequest(IOCRRequest request) { - String dirFolder = storageManager.getAndCreateStoragePath(request.getRequestId(), + storageManager.getAndCreateStoragePath(request.getRequestId(), request.getDocumentId(), null); byte[] image = downloadFile(request.getDownloadUrl()); @@ -103,41 +85,11 @@ public void processOCRRequest(IOCRRequest request) { // FIXME: send to monitoring app } - Text text = saveTextToFile(request.getRequestId(), request.getDocumentId(), ocrResult, request.getFilename(), ".txt"); - - String restEndpoint = propertyManager.getProperty(Properties.BASE_URL); - if (restEndpoint.endsWith("/")) { - restEndpoint = restEndpoint.substring(0, restEndpoint.length()-1); - } - - String fileEndpoint = restEndpoint + DownloadFileController.GET_FILE_URL - .replace(DownloadFileController.REQUEST_ID_PLACEHOLDER, request.getRequestId()) - .replace(DownloadFileController.DOCUMENT_ID_PLACEHOLDER, request.getDocumentId()) - .replace(DownloadFileController.FILENAME_PLACEHOLDER, text.filename); + RequestInfo info = saveTextToFile(request.getRequestId(), request.getDocumentId(), ocrResult, request.getFilename(), ".txt"); + info.setUploadId(request.getUploadId()); + info.setFileId(request.getFileId()); - ICompletedOCRRequest completedRequest = null; - try { - completedRequest = requestFactory.createRequest(request.getRequestId(), request.getUploadId()); - } catch (InstantiationException | IllegalAccessException e) { - logger.error("Could not create request.", e); - // this should never happen if used correctly - } - - completedRequest.setDocumentId(request.getDocumentId()); - completedRequest.setDownloadPath(text.path); - completedRequest.setSize(text.size); - completedRequest.setDownloadUrl(fileEndpoint); - completedRequest.setFilename(request.getFilename()); - completedRequest.setFileId(request.getFileId()); - completedRequest.setStatus(RequestStatus.COMPLETE); - completedRequest.setOcrDate(OffsetDateTime.now(ZoneId.of("UTC")).toString()); - completedRequest.setTextFilename(text.filename); - - try { - requestProducer.sendRequest(completedRequest, propertyManager.getProperty(Properties.KAFKA_TOPIC_OCR_COMPLETE)); - } catch (MessageCreationException e) { - logger.error("Could not send message.", e); - } + kafkaRequestSender.sendRequest(request.getRequestId(), request.getDocumentId(), info); } private byte[] downloadFile(String url) { @@ -161,7 +113,7 @@ private byte[] downloadFile(String url) { return null; } - protected Text saveTextToFile(String requestId, + protected RequestInfo saveTextToFile(String requestId, String documentId, String pageText, String filename, String fileExtentions) { String docFolder = storageManager.getAndCreateStoragePath(requestId, documentId, null); @@ -169,6 +121,7 @@ protected Text saveTextToFile(String requestId, if (!fileExtentions.startsWith(".")) { fileExtentions = "." + fileExtentions; } + String imageFilename = filename; filename = filename + fileExtentions; String filePath = docFolder + File.separator + filename; @@ -192,19 +145,6 @@ protected Text saveTextToFile(String requestId, } String relativePath = storageManager.getFileFolderPathInBaseFolder(requestId, documentId, null); - Text text = new Text(relativePath + File.separator + filename, fileObject.length(), filename); - return text; - } - - class Text { - public String path; - public long size; - public String filename; - - public Text(String path, long size, String filename) { - this.path = path; - this.size = size; - this.filename = filename; - } + return new RequestInfo(relativePath + File.separator + filename, fileObject.length(), imageFilename, filename); } } diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestInfo.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestInfo.java new file mode 100644 index 0000000..a32f92a --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestInfo.java @@ -0,0 +1,68 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl; + +public class RequestInfo { + + private String path; + private long size; + private String filename; + private String imageFilename; + private String uploadId; + private String fileId; + + public RequestInfo(String path, long size, String imageFilename, String filename) { + this.path = path; + this.size = size; + this.filename = filename; + this.imageFilename = imageFilename; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public String getImageFilename() { + return imageFilename; + } + + public void setImageFilename(String filename) { + this.imageFilename = filename; + } + + public String getUploadId() { + return uploadId; + } + + public void setUploadId(String uploadId) { + this.uploadId = uploadId; + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + +} diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestManager.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestManager.java new file mode 100644 index 0000000..10e77fa --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestManager.java @@ -0,0 +1,42 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IRequestManager; +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IRequestResender; + +@Service +public class RequestManager implements IRequestManager { + + @Autowired + private IRequestResender resender; + + private Future resendingResult; + + /* (non-Javadoc) + * @see edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.IRequestManager#startResendingRequests() + */ + @Override + public void startResendingRequests() { + resendingResult = resender.resendRequests(); + } + + /* (non-Javadoc) + * @see edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.IRequestManager#getResendingResults() + */ + @Override + public ResendingResult getResendingResults() throws InterruptedException, ExecutionException { + if (resendingResult == null) { + return new ResendingResult(0, null); + } + if (resendingResult.isDone()) { + return resendingResult.get(); + } + + return null; + } +} diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestResender.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestResender.java new file mode 100644 index 0000000..2ad9342 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/RequestResender.java @@ -0,0 +1,75 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl; + +import java.io.File; +import java.io.FilenameFilter; +import java.time.ZonedDateTime; +import java.util.concurrent.Future; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; +import org.springframework.stereotype.Service; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IKafkaRequestSender; +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IRequestResender; +import edu.asu.diging.gilesecosystem.util.files.IFileStorageManager; + +@Service +public class RequestResender implements IRequestResender { + + @Autowired + @Qualifier("fileStorageManager") + private IFileStorageManager storageManager; + + @Autowired + private IKafkaRequestSender kafkaRequestSender; + + /* (non-Javadoc) + * @see edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl.IRequestResender#resendRequests() + */ + + @Override + @Async + public Future resendRequests() { + String baseDir = storageManager.getBaseDirectoryWithFiletype(); + File baseDirFolder = new File(baseDir); + File[] requestFolders = baseDirFolder.listFiles(new DirectoryFilter()); + + int requestCounter = 0; + for (File requestFolder : requestFolders) { + String requestId = requestFolder.getName(); + File[] documentFolders = requestFolder.listFiles(new DirectoryFilter()); + // there should be just one + for (File documentFolder : documentFolders) { + String docId = documentFolder.getName(); + File[] textFiles = documentFolder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".txt"); + } + }); + + for (File textFile : textFiles) { + String relativePath = storageManager.getFileFolderPathInBaseFolder(requestId, docId, null); + String imageFilename = textFile.getName(); + imageFilename = imageFilename.substring(0, imageFilename.lastIndexOf(".")); + + RequestInfo info = new RequestInfo(relativePath + File.separator + textFile.getName(), textFile.length(), imageFilename, textFile.getName()); + kafkaRequestSender.sendRequest(requestId, docId, info); + requestCounter++; + } + } + } + + return new AsyncResult(new ResendingResult(requestCounter, ZonedDateTime.now())); + } + + private final class DirectoryFilter implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return new File(dir, name).isDirectory(); + } + } +} diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/ResendingResult.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/ResendingResult.java new file mode 100644 index 0000000..8176201 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/core/service/impl/ResendingResult.java @@ -0,0 +1,30 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.core.service.impl; + +import java.time.ZonedDateTime; + +public class ResendingResult { + + private int requestCount; + private ZonedDateTime completionTime; + + public ResendingResult(int requestCount, ZonedDateTime completionTime) { + super(); + this.requestCount = requestCount; + this.completionTime = completionTime; + } + + public int getRequestCount() { + return requestCount; + } + public void setRequestCount(int requestCount) { + this.requestCount = requestCount; + } + public ZonedDateTime getCompletionTime() { + return completionTime; + } + public void setCompletionTime(ZonedDateTime completionTime) { + this.completionTime = completionTime; + } + + +} diff --git a/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/web/RequestsController.java b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/web/RequestsController.java new file mode 100644 index 0000000..552bad6 --- /dev/null +++ b/cassiopeia/src/main/java/edu/asu/diging/gilesecosystem/cassiopeia/web/RequestsController.java @@ -0,0 +1,36 @@ +package edu.asu.diging.gilesecosystem.cassiopeia.web; + +import java.util.concurrent.ExecutionException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import edu.asu.diging.gilesecosystem.cassiopeia.core.service.IRequestManager; + +@Controller +public class RequestsController { + + @Autowired + private IRequestManager requestManager; + + @RequestMapping(value = "/admin/requests") + public String showRequestsPage(Model model) throws InterruptedException, ExecutionException { + if (requestManager.getResendingResults() == null) { + return "admin/requests/inProgress"; + } + model.addAttribute("resendResult", requestManager.getResendingResults()); + return "admin/requests"; + } + + @RequestMapping(value = "/admin/requests/resend", method = RequestMethod.POST) + public String resendMessages(Model model) throws InterruptedException, ExecutionException { + if (requestManager.getResendingResults() == null) { + return "admin/requests/inProgress"; + } + requestManager.startResendingRequests(); + return "redirect:/admin/requests"; + } +} diff --git a/cassiopeia/src/main/webapp/WEB-INF/spring/root-context.xml b/cassiopeia/src/main/webapp/WEB-INF/spring/root-context.xml index 80d76ac..3f227c5 100644 --- a/cassiopeia/src/main/webapp/WEB-INF/spring/root-context.xml +++ b/cassiopeia/src/main/webapp/WEB-INF/spring/root-context.xml @@ -30,5 +30,8 @@ + + + diff --git a/cassiopeia/src/main/webapp/WEB-INF/tiles-defs.xml b/cassiopeia/src/main/webapp/WEB-INF/tiles-defs.xml index c6edafe..da092f1 100644 --- a/cassiopeia/src/main/webapp/WEB-INF/tiles-defs.xml +++ b/cassiopeia/src/main/webapp/WEB-INF/tiles-defs.xml @@ -22,5 +22,12 @@ + + + + + + + \ No newline at end of file diff --git a/cassiopeia/src/main/webapp/WEB-INF/tiles/skeleton.jsp b/cassiopeia/src/main/webapp/WEB-INF/tiles/skeleton.jsp index fd6970c..136672b 100644 --- a/cassiopeia/src/main/webapp/WEB-INF/tiles/skeleton.jsp +++ b/cassiopeia/src/main/webapp/WEB-INF/tiles/skeleton.jsp @@ -57,6 +57,9 @@
  • ">System Config
  • +
  • + ">Requests +
  • diff --git a/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests.jsp b/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests.jsp new file mode 100644 index 0000000..abdd103 --- /dev/null +++ b/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests.jsp @@ -0,0 +1,14 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + +

    Requests

    + +

    +Last resending: ${resendResult.completionTime} +
    +Resent request count: ${resendResult.requestCount} +

    + + + + \ No newline at end of file diff --git a/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests_inprogress.jsp b/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests_inprogress.jsp new file mode 100644 index 0000000..ea4abe3 --- /dev/null +++ b/cassiopeia/src/main/webapp/WEB-INF/views/admin/requests_inprogress.jsp @@ -0,0 +1,8 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + +

    Requests Resending in progress

    + +

    +Requests are being resend. +

    \ No newline at end of file