diff --git a/README.md b/README.md new file mode 100644 index 0000000..5075c32 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# SpringBootApplications + +1) All the RestTemplate tests were implemented in RestClientTest.java using Junit. +After you start the application, you can run RestClientTest.java. + +Please remember that test methods have to be executed with the given order because results of one method is affecting the result of another one. +For example; we need to upload first to be able to make a download, get by id etc. + +2) CommonUtils class was added to define a few utility methods. + +3) Email, EmailSender, ScheduledCronJobTests classes were created to send emails + +4) Application.java, FileController.java, FileMetaDataSearchCriteria.java, FileUploadUtil.java, RESTClient.java, RestClientTest.java, +ScheduledCronJobs.java were updated. + + +Important notes: + +For testing purposes, I assume that files in 'C://UploadRepo' and 'C://UploadRepo' folder itself will not be removed manually while the application is running. + +Please run RestClientTest.java to run the unit tests. The tests are connected to each other. Please make sure you run the whole class(not single methods) for the convenience of tests. + +Please run RestClientTest.java once for each run on Application.java. Otherwise, the test results will be unexpected due to uploading duplicate name files. + +Please replace the email addresses in application.properties with your own email addresses to see whether the cron job is sending an email. It would be a better practice to keep these type of values in a SYSTEM_PARAMETERS table. +Application.createJavaMailSender needs update in terms of SMTP settings. + +Please check 'src/main/resourcec/DownloadRepository' and 'C://UploadRepo' for downloaded and uploaded files. diff --git a/SpringRESTExample/pom.xml b/SpringRESTExample/pom.xml index dea964b..5c7d277 100644 --- a/SpringRESTExample/pom.xml +++ b/SpringRESTExample/pom.xml @@ -83,6 +83,34 @@ spring-boot-starter-cloud-connectors + + com.jayway.jsonpath + json-path-assert + + + org.skyscreamer + jsonassert + + + org.springframework.boot + + spring-boot-starter-data-elasticsearch + + + + org.springframework.boot + spring-boot-starter-artemis + + + org.springframework.boot + spring-boot-starter-freemarker + + + + javax.mail + mail + 1.4.7 + diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/Application.java b/SpringRESTExample/src/main/java/com/springboot/restapi/Application.java index 3497f0b..31880b6 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/Application.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/Application.java @@ -1,9 +1,12 @@ package com.springboot.restapi; +import java.util.Properties; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.web.client.RestTemplate; @SpringBootApplication @@ -14,72 +17,20 @@ public static void main(String[] args) { args); /** - * This method call will create a the folder "C://UploadRepo". This - * folder will be used to save the content of uploaded files. + * This method call will create the folder "C://UploadRepo". This folder + * will be used to save the content of the uploaded files. */ ((FileUploadUtil) context.getBean("fileUploadUtil")) .createUploadRepository(); - System.out.println(((RESTClient) context.getBean("restClient")) - .getAllFiles()); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile( - "src/main/resources/UploadFromRepository/TextFile.txt", - null, null)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .getAllFiles()); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile( - "src/main/resources/UploadFromRepository/TextFile.txt", - "TextFile_1.txt", null)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile( - "src/main/resources/UploadFromRepository/TextFile.txt", - "TextFile_2.txt", "Test")); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile("C://FileRepo/TextFileee.txt", "TextFile_3.txt", - "Test")); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile("C://FileRepo///", "TextFile_4.txt", "Test")); - - System.out.println(((RESTClient) context.getBean("restClient")) - .uploadFile("C://FileRepo/TextFile.txt", "Text//File_5.txt", - "Test")); - - System.out.println(((RESTClient) context.getBean("restClient")) - .getAllFiles()); - - System.out.println(((RESTClient) context.getBean("restClient")) - .getFileMetaDataById(1)); + System.out + .println("Important: For testing purposes, I assume that files in 'C://UploadRepo' and 'C://UploadRepo' folder itself will not be removed manually while the application is running."); - System.out.println(((RESTClient) context.getBean("restClient")) - .getFileMetaDataById(null)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .getFileMetaDataById(100)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .downloadFile(1)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .downloadFile(null)); - - System.out.println(((RESTClient) context.getBean("restClient")) - .downloadFile(100)); - - FileMetaDataSearchCriteria fileMetaDataSearchCriteria = new FileMetaDataSearchCriteria(); - - fileMetaDataSearchCriteria.setName("File_1"); - - System.out.println(((RESTClient) context.getBean("restClient")) - .searchFiles(fileMetaDataSearchCriteria)); + System.out + .println("Important: Please run RestClientTest.java to run the unit tests. The tests are connected to each other. Please make sure you run the whole class(not single methods) for the convenience of tests."); + System.out + .println("Important: Please run RestClientTest.java once for each run on Application.java. Otherwise, the test results will be unexpected due to uploading duplicate name files."); } @Bean @@ -87,4 +38,25 @@ public RestTemplate getRestTemplate() { return new RestTemplate(); } + /** + * This part may need update due to the network settings in the machine that + * code is executed. + * + * @return + */ + @Bean + public JavaMailSenderImpl createJavaMailSender() { + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); + javaMailSender.setHost("smtp.gmail.com"); + javaMailSender.setPort(587); + Properties properties = new Properties(); + properties.put("spring.mail.username", "*****"); + properties.put("spring.mail.password", "*****"); + + properties.put("spring.mail.properties.mail.smtp.auth", true); + properties + .put("spring.mail.properties.mail.smtp.starttls.enable", true); + return javaMailSender; + } + } diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/CommonUtils.java b/SpringRESTExample/src/main/java/com/springboot/restapi/CommonUtils.java new file mode 100644 index 0000000..d7c0f87 --- /dev/null +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/CommonUtils.java @@ -0,0 +1,108 @@ +package com.springboot.restapi; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class CommonUtils { + + @Value("${run_scheduled_jobs}") + private String runScheduleJobs; + + @Value("${from_address}") + private String mailFromAddress; + + @Value("${to_address}") + private String mailToAddress; + + @Value("${reply_to_address}") + private String replyToAddress; + + /** + * This method is to convert a JSONArray to JSONObject list + * + * @param array + * @return + * @throws JSONException + */ + public List getJSONObjectListFromJSONArray(JSONArray array) + throws JSONException { + ArrayList jsonObjects = new ArrayList(); + for (int i = 0; i < (array != null ? array.length() : 0); jsonObjects + .add(array.getJSONObject(i++))) + ; + return jsonObjects; + } + + /** + * Returns whether or not a scheduled job is set to run based on the + * property value "run_scheduled_jobs" + * + * @return + */ + public boolean runScheduledJob() { + return Boolean.parseBoolean(runScheduleJobs); + } + + /** + * Returns the to_address entry in defined application.properties + * + * @return + */ + public String getMailToAddress() { + return mailToAddress; + } + + /** + * Returns the reply_to_address in defined application.properties + * + * @return + */ + public String getMailReplyToAddress() { + return replyToAddress; + } + + /** + * Returns the from_address in defined application.properties + * + * @return + */ + public String getMailFromAddress() { + return mailFromAddress; + } + + /** + * This method is to create an Email object from a list of FileMetaData . + * + * @param fileMetaDatas + * @return + */ + public Email generateEmailFrom(List fileMetaDatas) { + Email email = new Email(); + if (fileMetaDatas == null || fileMetaDatas.size() == 0) { + email.setSubject("No FileMetaData records added in last one hour"); + } else { + email.setSubject("New FileMetaData records added in last one hour: " + + fileMetaDatas.size()); + } + email.setFromAddress(getMailFromAddress()); + email.setToAddresses(getMailToAddress()); + email.setReplyTo(getMailReplyToAddress()); + + StringBuilder body = new StringBuilder(); + + for (FileMetaData fileMetaData : fileMetaDatas) { + body.append("

"); + body.append(fileMetaData.toString()); + body.append("

"); + } + email.setBody(body.toString()); + return email; + } +} diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/Email.java b/SpringRESTExample/src/main/java/com/springboot/restapi/Email.java new file mode 100644 index 0000000..5c52252 --- /dev/null +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/Email.java @@ -0,0 +1,85 @@ +package com.springboot.restapi; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +/** + * This class can be turned into an entity to store all email notifications in + * database. In that case, we will need to add more columns like sent_status, + * created_date, sent_date etc.Also we can write a separate scheduler to poll + * the emails that are waiting to be sent. + */ +public class Email { + private String fromAddress; + private String replyTo; + private String toAddresses; + private String ccAddresses; + private String bccAddresses; + private String subject; + private String body; + + public String getFromAddress() { + return fromAddress; + } + + public void setFromAddress(String fromAddress) { + this.fromAddress = fromAddress; + } + + public String getReplyTo() { + return replyTo; + } + + public void setReplyTo(String replyTo) { + this.replyTo = replyTo; + } + + public String getToAddresses() { + return toAddresses; + } + + public void setToAddresses(String toAddresses) { + this.toAddresses = toAddresses; + } + + public String getCcAddresses() { + return ccAddresses; + } + + public void setCcAddresses(String ccAddresses) { + this.ccAddresses = ccAddresses; + } + + public String getBccAddresses() { + return bccAddresses; + } + + public void setBccAddresses(String bccAddresses) { + this.bccAddresses = bccAddresses; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + @Override + public String toString() { + return new ToStringBuilder(this).appendSuper(super.toString()) + .append("fromAddress", fromAddress).append("replyTo", replyTo) + .append("toAddresses", toAddresses) + .append("ccAddresses", ccAddresses) + .append("bccAddresses", bccAddresses) + .append("subject", subject).append("body", body).toString(); + } +} \ No newline at end of file diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/EmailSender.java b/SpringRESTExample/src/main/java/com/springboot/restapi/EmailSender.java new file mode 100644 index 0000000..6b8b584 --- /dev/null +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/EmailSender.java @@ -0,0 +1,125 @@ +package com.springboot.restapi; + +import javax.inject.Inject; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Component; + +/** + * Utility to send email using JavaMailSender. + */ +@Component +public class EmailSender { + + private static final Logger logger = LoggerFactory + .getLogger(EmailSender.class); + + private static final char EMAIL_ADDR_SEPERATOR = ';'; + + @Inject + private JavaMailSender javaMailSender; + + /** + * Sends email using JavaMailSender. + * + * @param email + * the email + */ + public void sendEmail(Email email) { + if (logger.isTraceEnabled()) { + logger.trace("sendEmail(Email) - start"); + } + + try { + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + MimeMessageHelper helper; + + helper = new MimeMessageHelper(mimeMessage, true); + + prepareMimeMessageHelper(helper, email); + javaMailSender.send(mimeMessage); + logger.info("Email with subject: {} sent successfully", + email.getSubject()); + } catch (MessagingException e) { + logger.error("sendEmail(Email)", e); + } + + if (logger.isTraceEnabled()) { + logger.trace("sendEmail(Email) - end"); + } + } + + /** + * Prepare mime message. + * + * @param helper + * the MimeMessageHelper + * @param email + * the email + * @throws MessagingException + * the messaging exception + */ + private void prepareMimeMessageHelper(MimeMessageHelper helper, Email email) + throws MessagingException { + if (logger.isTraceEnabled()) { + logger.trace("prepareMimeMessageHelper(MimeMessageHelper, Email) - start"); + } + + String[] toAddresses = getEmailAddressesArray(email.getToAddresses()); + String[] ccAddresses = getEmailAddressesArray(email.getCcAddresses()); + String[] bccAddresses = getEmailAddressesArray(email.getBccAddresses()); + String subject = email.getSubject(); + String body = email.getBody(); + + helper.setFrom(email.getFromAddress()); + helper.setTo(toAddresses); + helper.setReplyTo(email.getReplyTo()); + + if (!ArrayUtils.isEmpty(ccAddresses)) { + helper.setCc(ccAddresses); + } + + if (!ArrayUtils.isEmpty(bccAddresses)) { + helper.setBcc(bccAddresses); + } + + helper.setSubject(subject); + /** + * Send as html + */ + helper.setText(body, true); + + if (logger.isTraceEnabled()) { + logger.trace("prepareMimeMessageHelper(MimeMessageHelper, Email) - end"); + } + } + + /** + * Gets the email addresses. + * + * @param toAddresses + * the to addresses + * @return the email addresses array + */ + private String[] getEmailAddressesArray(String toAddresses) { + if (logger.isTraceEnabled()) { + logger.trace("getEmailAddressesArray(String) - start"); + } + + String[] emailAddresses = StringUtils.split(toAddresses, + EMAIL_ADDR_SEPERATOR); + + if (logger.isTraceEnabled()) { + logger.trace("getEmailAddressesArray(String) - end"); + } + return emailAddresses; + } + +} diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/FileController.java b/SpringRESTExample/src/main/java/com/springboot/restapi/FileController.java index 692845d..5decb7f 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/FileController.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/FileController.java @@ -5,13 +5,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Collection; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -39,17 +35,8 @@ public class FileController { * @return */ @RequestMapping(value = "/files", method = RequestMethod.GET) - public ResponseEntity getAllFiles() { - List resultList = null; - try { - resultList = repo.findAll(); - } catch (Exception e) { - return new ResponseEntity( - "Exception: getAllFiles has failed.", - HttpStatus.BAD_REQUEST); - } - return new ResponseEntity>(resultList, - HttpStatus.OK); + public List getAllFiles() { + return repo.findAll(); } /** @@ -73,10 +60,21 @@ public List searchFiles( * @return */ @RequestMapping(value = "/metadata/{id}", method = RequestMethod.GET) - public ResponseEntity getFileMetaData(@PathVariable Integer id) { + public FileMetaData getFileMetaData(@PathVariable Integer id) { FileMetaData fileMetaData = repo.findOne(id); if (fileMetaData != null) { + /** + * The reason I update some metadata fileds here is that since we + * are storing the file content in disk, I thought it might be + * helpful to update the last update and last access times and some + * other fields when we make a request to get a metadata by id. + * + * But honestly, I think we can do the same thing by a scheduled job + * which runs every 15 minutes to update some attributes of the + * related FileMetaData according to the metadata of the file on + * disk + */ try { Path path = Paths.get(fileMetaData.getPath()); BasicFileAttributes attr = Files.readAttributes(path, @@ -92,28 +90,19 @@ public ResponseEntity getFileMetaData(@PathVariable Integer id) { fileMetaData.setSymbolicLink(attr.isSymbolicLink()); fileMetaData.setSize(attr.size()); repo.saveAndFlush(fileMetaData); - - return new ResponseEntity(fileMetaData, - HttpStatus.OK); + return fileMetaData; } } catch (IOException e) { e.printStackTrace(); - return new ResponseEntity( - "IOException: Get file meta-data process has failed.", - HttpStatus.BAD_REQUEST); } catch (Exception e) { e.printStackTrace(); - return new ResponseEntity( - "Exception: Request has failed.", - HttpStatus.BAD_REQUEST); } } - return new ResponseEntity("No FileMetaData found with id: " - + id, HttpStatus.BAD_REQUEST); + return null; } /** @@ -126,27 +115,27 @@ public ResponseEntity getFileMetaData(@PathVariable Integer id) { * @return */ @RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data") - public ResponseEntity uploadFile( - @RequestParam("file") MultipartFile file, + public String uploadFile(@RequestParam("file") MultipartFile file, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "descr", required = false) String descr) { if (file.isEmpty()) { - return new ResponseEntity( - "Please select a file to upload.", HttpStatus.BAD_REQUEST); + return "Empty file: Please select a file to upload."; } String fileName = StringUtils.isEmpty(name) ? file .getOriginalFilename() : name; - try { + if (!fileUploadUtil.isFilenameValid(fileName)) { + return "Invalid file name : " + fileName; + } + try { if (!StringUtils.isEmpty(fileName)) { List metaDataList = repo.findByName(fileName); if (metaDataList != null && metaDataList.size() > 0) { - return new ResponseEntity( - "Duplicate file name - FileMetaData found for the given name: " - + fileName, HttpStatus.BAD_REQUEST); + return "Duplicate file name: An existing FileMetaData found for the given name " + + fileName; } } @@ -158,13 +147,11 @@ public ResponseEntity uploadFile( } catch (Exception e) { - return new ResponseEntity(e.getClass() - + ": File upload process has failed for the file " - + fileName, HttpStatus.BAD_REQUEST); + return "Failed to upload " + fileName + " -- Exception: " + + e.toString(); } - return new ResponseEntity(fileName + " successfully uploaded", - new HttpHeaders(), HttpStatus.OK); + return "You have successfully uploaded " + fileName; } /** @@ -175,7 +162,7 @@ public ResponseEntity uploadFile( * @return */ @RequestMapping(value = "/download/{id}", method = RequestMethod.GET) - public ResponseEntity downloadFile(@PathVariable("id") Integer id) { + public String downloadFile(@PathVariable("id") Integer id) { FileMetaData fileMetaData = repo.findOne(id); if (fileMetaData != null) { try { @@ -186,21 +173,18 @@ public ResponseEntity downloadFile(@PathVariable("id") Integer id) { Paths.get("src/main/resources/DownloadRepository/" + fileMetaData.getName()), data); - return new ResponseEntity( - "File successfully downloaded.", new HttpHeaders(), - HttpStatus.OK); + return "You have successfully downloaded the file ID: " + id + + ", Name: " + fileMetaData.getName(); } catch (Exception e) { e.printStackTrace(); - return new ResponseEntity(e.getClass() - + ": Download file process has failed", - HttpStatus.BAD_REQUEST); + return "Failed to download the file " + id + " --- Exception: " + + e.toString(); } } - return new ResponseEntity("File not found to download", - HttpStatus.BAD_REQUEST); + return "No FileMetaData found with ID: " + id; } } diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/FileMetaDataSearchCriteria.java b/SpringRESTExample/src/main/java/com/springboot/restapi/FileMetaDataSearchCriteria.java index d54da5e..f207eb4 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/FileMetaDataSearchCriteria.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/FileMetaDataSearchCriteria.java @@ -2,6 +2,8 @@ import java.time.LocalDateTime; +import org.apache.commons.lang3.builder.ToStringBuilder; + public class FileMetaDataSearchCriteria { private String name; @@ -124,4 +126,18 @@ public LocalDateTime getLastAccessDateTo() { public void setLastAccessDateTo(LocalDateTime lastAccessDateTo) { this.lastAccessDateTo = lastAccessDateTo; } + + @Override + public String toString() { + return new ToStringBuilder(this).appendSuper(super.toString()) + .append("name", name).append("descr", descr) + .append("path", path) + .append("createdDateFrom", createdDateFrom) + .append("createdDateTo", createdDateTo) + .append("lastUpdateDateFrom", lastUpdateDateFrom) + .append("lastUpdateDateTo", lastUpdateDateTo) + .append("contentType", contentType) + .append("lastAccessDateFrom", lastAccessDateFrom) + .append("lastAccessDateTo", lastAccessDateTo).toString(); + } } diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/FileUploadUtil.java b/SpringRESTExample/src/main/java/com/springboot/restapi/FileUploadUtil.java index 8cdb10e..5df794d 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/FileUploadUtil.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/FileUploadUtil.java @@ -36,7 +36,6 @@ public void createUploadRepository() { + DEFAULT_UPLOADED_FOLDER + "!"); } } - System.out .println("Please see the 'C:' directory for the new created UploadRepo folder that will be used as the default upload repository."); @@ -111,7 +110,7 @@ public FileMetaData createFileMetaDataFrom(String name, String descr, return fileMetaData; } - public static LocalDateTime createLocalDateTimeFrom(FileTime fileTime) { + public LocalDateTime createLocalDateTimeFrom(FileTime fileTime) { if (fileTime != null) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(fileTime.toMillis()); @@ -124,4 +123,13 @@ public static LocalDateTime createLocalDateTimeFrom(FileTime fileTime) { return null; } + /** + * This method is to check whether the given name is a valid file name + * + * @param file + * @return + */ + public boolean isFilenameValid(String file) { + return !file.matches("^.*[^a-zA-Z0-9._-].*$"); + } } diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/RESTClient.java b/SpringRESTExample/src/main/java/com/springboot/restapi/RESTClient.java index 2c8ae2d..a3c0dcf 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/RESTClient.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/RESTClient.java @@ -1,7 +1,5 @@ package com.springboot.restapi; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; @@ -24,11 +22,12 @@ public class RESTClient { * * @return */ - public String getAllFiles() { - String result = restTemplate.getForObject( + public ResponseEntity getAllFiles() { + ResponseEntity response = restTemplate.getForEntity( "http://localhost:8080/files", String.class); - - return result; + System.out.println("http://localhost:8080/files" + " ---> " + + response.toString()); + return response; } /** @@ -38,12 +37,16 @@ public String getAllFiles() { * @param fileMetaDataSearchCriteria * @return */ - public String searchFiles( + public ResponseEntity searchFiles( FileMetaDataSearchCriteria fileMetaDataSearchCriteria) { HttpEntity request = new HttpEntity<>( fileMetaDataSearchCriteria); - return restTemplate.postForObject("http://localhost:8080/searchFile", - request, List.class).toString(); + ResponseEntity response = restTemplate.postForEntity( + "http://localhost:8080/searchFile", request, String.class); + System.out.println("http://localhost:8080/searchFile" + + " Search Criteria: " + fileMetaDataSearchCriteria.toString() + + " ---> " + response.toString()); + return response; } /** @@ -55,7 +58,8 @@ public String searchFiles( * @param descr * @return */ - public String uploadFile(String filePath, String name, String descr) { + public ResponseEntity uploadFile(String filePath, String name, + String descr) { try { MultiValueMap parameters = new LinkedMultiValueMap(); parameters.add("file", new FileSystemResource(filePath)); @@ -66,16 +70,22 @@ public String uploadFile(String filePath, String name, String descr) { headers.set("Content-Type", "multipart/form-data"); headers.set("Accept", "text/plain"); - String result = restTemplate.postForObject( + ResponseEntity response = restTemplate.postForEntity( "http://localhost:8080/upload", new HttpEntity>(parameters, headers), String.class); - - return result; + System.out.println("http://localhost:8080/upload" + " file: " + + filePath + ", name: " + name + ", descr: " + descr + + " ---> " + response.toString()); + return response; } catch (Exception e) { - return new ResponseEntity("uploadFile('" + filePath - + "', '" + name + "', '" + descr + "') has failed.", - HttpStatus.BAD_REQUEST).toString(); + ResponseEntity excResponse = new ResponseEntity( + "uploadFile('" + filePath + "', '" + name + "', '" + descr + + "') has failed.", HttpStatus.BAD_REQUEST); + System.out.println("http://localhost:8080/upload" + " file: " + + filePath + ", name: " + name + ", descr: " + descr + + " ---> " + excResponse.toString()); + return excResponse; } } @@ -86,16 +96,21 @@ public String uploadFile(String filePath, String name, String descr) { * @param id * @return */ - public String getFileMetaDataById(Integer id) { + public ResponseEntity getFileMetaDataById(Integer id) { try { - String result = restTemplate.getForObject( + ResponseEntity response = restTemplate.getForEntity( "http://localhost:8080/metadata/" + id, String.class); + System.out.println("http://localhost:8080/metadata/" + id + + " ---> " + response.toString()); - return result; + return response; } catch (Exception e) { - return new ResponseEntity("getFileMetaDataById(" + id - + ") has failed.", HttpStatus.BAD_REQUEST).toString(); - + ResponseEntity excResponse = new ResponseEntity( + "getFileMetaDataById(" + id + ") has failed.", + HttpStatus.BAD_REQUEST); + System.out.println("http://localhost:8080/metadata/" + id + + " ---> " + excResponse.toString()); + return excResponse; } } @@ -106,18 +121,24 @@ public String getFileMetaDataById(Integer id) { * @param id * @return */ - public String downloadFile(Integer id) { + public ResponseEntity downloadFile(Integer id) { try { restTemplate.getMessageConverters().add( new ByteArrayHttpMessageConverter()); - return restTemplate.getForObject("http://localhost:8080/download/" - + id, String.class); + ResponseEntity response = restTemplate.getForEntity( + "http://localhost:8080/download/" + id, String.class); + System.out.println("http://localhost:8080/download/" + id + + " ---> " + response.toString()); + return response; } catch (Exception e) { - return new ResponseEntity("downloadFile(" + id - + ") has failed.", HttpStatus.BAD_REQUEST).toString(); + ResponseEntity excResponse = new ResponseEntity("downloadFile(" + id + + ") has failed.", HttpStatus.BAD_REQUEST); + System.out.println("http://localhost:8080/download/" + id + + " ---> " + excResponse.toString()); + return excResponse; } } } diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/RestClientTest.java b/SpringRESTExample/src/main/java/com/springboot/restapi/RestClientTest.java index 0194e5e..a89af7a 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/RestClientTest.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/RestClientTest.java @@ -1,23 +1,210 @@ package com.springboot.restapi; +import java.util.List; + +import org.hamcrest.CoreMatchers; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * RestTemplate tests in Application.java need to be implemented here. * + * The tests are connected to each other. Please make sure you run the whole + * class(not single methods) for the convenience of tests. + * + * Please run RestClientTest.java once for each run on Application.java. + * Otherwise, the test results will be unexpected due to uploading duplicate + * name files. + * */ @RunWith(value = SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class RestClientTest { @Autowired private RESTClient restClient; + @Autowired + private CommonUtils commonUtils; + + @Test + public void createFileTests() { + Assert.assertEquals(restClient.getAllFiles().getBody(), "[]"); + ResponseEntity uploadResponse_1 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", null, + null); + ResponseEntity uploadResponse_2 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "TextFile_1.txt", null); + ResponseEntity uploadResponse_3 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "TextFile_2.txt", "Test"); + + ResponseEntity uploadResponse_4 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFileee.txt", + "TextFile_3.txt", "Test"); + + ResponseEntity uploadResponse_5 = restClient.uploadFile( + "src/main/resources//UploadFromRepository////", + "TextFile_4.txt", "Test"); + + // Invalid file name + ResponseEntity uploadResponse_6 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "Text//File_5.txt", "Test"); + + // Invalid file name + ResponseEntity uploadResponse_7 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "Text/File_5.txt", "Test"); + + // Invalid file name + ResponseEntity uploadResponse_8 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "Text\\File_5.txt", "Test"); + + // Duplicate file name + ResponseEntity uploadResponse_9 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TextFile.txt", + "TextFile_2.txt", "Test"); + + ResponseEntity uploadResponse_10 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TestPDF.pdf", + "FirstTestPDF.pdf", "Test"); + + ResponseEntity uploadResponse_11 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TestExcel.xlsx", + "FirstTestExcel.xlsx", "Test"); + + ResponseEntity uploadResponse_12 = restClient.uploadFile( + "src/main/resources/UploadFromRepository/TestWord.docx", + "FirstTestWord.docx", "Test"); + + Assert.assertThat(uploadResponse_1.getBody(), + CoreMatchers.containsString("successfully")); + Assert.assertThat(uploadResponse_2.getBody(), + CoreMatchers.containsString("successfully")); + Assert.assertThat(uploadResponse_3.getBody(), + CoreMatchers.containsString("successfully")); + + Assert.assertEquals(uploadResponse_4.getStatusCode(), + HttpStatus.BAD_REQUEST); + + Assert.assertEquals(uploadResponse_5.getStatusCode(), + HttpStatus.BAD_REQUEST); + + Assert.assertThat(uploadResponse_6.getBody(), + CoreMatchers.containsString("Invalid file name")); + + Assert.assertThat(uploadResponse_7.getBody(), + CoreMatchers.containsString("Invalid file name")); + + Assert.assertThat(uploadResponse_8.getBody(), + CoreMatchers.containsString("Invalid file name")); + + Assert.assertThat(uploadResponse_9.getBody(), + CoreMatchers.containsString("Duplicate")); + + Assert.assertThat(uploadResponse_10.getBody(), + CoreMatchers.containsString("successfully")); + + Assert.assertThat(uploadResponse_11.getBody(), + CoreMatchers.containsString("successfully")); + + Assert.assertThat(uploadResponse_12.getBody(), + CoreMatchers.containsString("successfully")); + } + @Test - public void testFindByName() { + public void downloadFileTests() { + Assert.assertEquals(restClient.downloadFile(null).getStatusCode(), + HttpStatus.BAD_REQUEST); + + ResponseEntity response = restClient.getAllFiles(); + + JSONArray jsonArray = new JSONArray(response.getBody()); + List jsonObjectList = commonUtils + .getJSONObjectListFromJSONArray(jsonArray); + for (JSONObject jsonObject : jsonObjectList) { + Assert.assertThat(restClient.downloadFile(jsonObject.getInt("id")) + .getBody(), CoreMatchers.containsString("successfully")); + + } + + Assert.assertThat(restClient.downloadFile(100).getBody(), + CoreMatchers.startsWith("No FileMetaData")); + } + + @Test + public void getFileMetaDataTests() { + Assert.assertEquals(restClient.getFileMetaDataById(null) + .getStatusCode(), HttpStatus.BAD_REQUEST); + Assert.assertEquals(restClient.getFileMetaDataById(1).getStatusCode(), + HttpStatus.OK); + Assert.assertEquals( + restClient.getFileMetaDataById(100).getStatusCode(), + HttpStatus.OK); + } + + @Test + public void getFilesTests() { + ResponseEntity response_1 = restClient.getAllFiles(); + Assert.assertEquals(response_1.getStatusCode(), HttpStatus.OK); + + JSONArray jsonArray = new JSONArray(response_1.getBody()); + List jsonObjectList = commonUtils + .getJSONObjectListFromJSONArray(jsonArray); + Assert.assertTrue(jsonObjectList.size() != 0); + } + + @Test + public void searchFileTests() { + FileMetaDataSearchCriteria searchCriteria_1 = new FileMetaDataSearchCriteria(); + searchCriteria_1.setName("XXXX"); + ResponseEntity response_1 = restClient + .searchFiles(searchCriteria_1); + + JSONArray jsonArray_1 = new JSONArray(response_1.getBody()); + List jsonObjectList_1 = commonUtils + .getJSONObjectListFromJSONArray(jsonArray_1); + + Assert.assertEquals(response_1.getStatusCode(), HttpStatus.OK); + Assert.assertTrue(jsonObjectList_1.size() == 0); + + FileMetaDataSearchCriteria searchCriteria_2 = new FileMetaDataSearchCriteria(); + searchCriteria_2.setName("TextFile"); + ResponseEntity response_2 = restClient + .searchFiles(searchCriteria_2); + + JSONArray jsonArray_2 = new JSONArray(response_2.getBody()); + List jsonObjectList_2 = commonUtils + .getJSONObjectListFromJSONArray(jsonArray_2); + + Assert.assertEquals(response_2.getStatusCode(), HttpStatus.OK); + Assert.assertTrue(jsonObjectList_2.size() > 1); + + FileMetaDataSearchCriteria searchCriteria_3 = new FileMetaDataSearchCriteria(); + searchCriteria_3.setContentType("WEIRD_CONTENT"); + ResponseEntity response_3 = restClient + .searchFiles(searchCriteria_3); + + JSONArray jsonArray_3 = new JSONArray(response_3.getBody()); + List jsonObjectList_3 = commonUtils + .getJSONObjectListFromJSONArray(jsonArray_3); + + Assert.assertEquals(response_3.getStatusCode(), HttpStatus.OK); + Assert.assertTrue(jsonObjectList_3.size() == 0); + } } \ No newline at end of file diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobTests.java b/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobTests.java new file mode 100644 index 0000000..982cf74 --- /dev/null +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobTests.java @@ -0,0 +1,21 @@ +package com.springboot.restapi; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(value = SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = Application.class) +public class ScheduledCronJobTests { + + @Autowired + private ScheduledCronJobs scheduledCronJobs; + + + @Test + public void pollForNewItemsTest(){ + scheduledCronJobs.pollForNewItems(); + } +} diff --git a/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobs.java b/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobs.java index c64ddd3..41838fb 100644 --- a/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobs.java +++ b/SpringRESTExample/src/main/java/com/springboot/restapi/ScheduledCronJobs.java @@ -1,36 +1,66 @@ package com.springboot.restapi; +import java.time.LocalDateTime; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +@Service public class ScheduledCronJobs { private AtomicBoolean isRunning = new AtomicBoolean(false); + @Autowired + private FileMetaDataDao fileMetaDataDao; + + @Autowired + private CommonUtils commonUtils; + + @Autowired + private EmailSender emailSender; + /** * Scheduled job to poll for new items in the last hour and send an email + * + * Please remember that emailSender may give an error due to the SMTP + * configuration settings or any network restrictions in the machine that + * the code is executed. */ @Scheduled(cron = "0 0/60 * * *") public void pollForNewItems() { + if (!commonUtils.runScheduledJob()) { + System.out.println("Not running scheduled jobs on this container."); + return; + } if (isRunning.get()) { + System.out + .println("pollForNewItems cron job process is already running so exiting"); return; } + List fileMetaDataList = null; try { isRunning.set(true); - /** - * Add the business logic here to fetch the items in the last hour - */ + FileMetaDataSearchCriteria fileMetaDataSearchCriteria = new FileMetaDataSearchCriteria(); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime oneHourAgo = now.minusHours(1); + fileMetaDataSearchCriteria.setCreatedDateFrom(oneHourAgo); + fileMetaDataSearchCriteria.setCreatedDateTo(now); + + fileMetaDataList = fileMetaDataDao + .searchFileMetaData(fileMetaDataSearchCriteria); } catch (Exception e) { e.printStackTrace(); } finally { isRunning.set(false); - /** - * Send email if there are any new items added in the last hour - */ + if (fileMetaDataList != null) { + Email email = commonUtils.generateEmailFrom(fileMetaDataList); + emailSender.sendEmail(email); + } } } - } diff --git a/SpringRESTExample/src/main/resources/UploadFromRepository/TestExcel.xlsx b/SpringRESTExample/src/main/resources/UploadFromRepository/TestExcel.xlsx new file mode 100644 index 0000000..099b218 Binary files /dev/null and b/SpringRESTExample/src/main/resources/UploadFromRepository/TestExcel.xlsx differ diff --git a/SpringRESTExample/src/main/resources/UploadFromRepository/TestPDF.pdf b/SpringRESTExample/src/main/resources/UploadFromRepository/TestPDF.pdf new file mode 100644 index 0000000..4c229a0 Binary files /dev/null and b/SpringRESTExample/src/main/resources/UploadFromRepository/TestPDF.pdf differ diff --git a/SpringRESTExample/src/main/resources/UploadFromRepository/TestWord.docx b/SpringRESTExample/src/main/resources/UploadFromRepository/TestWord.docx new file mode 100644 index 0000000..b09f25d Binary files /dev/null and b/SpringRESTExample/src/main/resources/UploadFromRepository/TestWord.docx differ diff --git a/SpringRESTExample/src/main/resources/application.properties b/SpringRESTExample/src/main/resources/application.properties index e69de29..037f8dd 100644 --- a/SpringRESTExample/src/main/resources/application.properties +++ b/SpringRESTExample/src/main/resources/application.properties @@ -0,0 +1,5 @@ +run_scheduled_jobs=true +send_emails=true +from_address=********* +to_address=********* +reply_to_address=********* diff --git a/SpringRESTExample/src/main/resources/mail.properties b/SpringRESTExample/src/main/resources/mail.properties new file mode 100644 index 0000000..a44ed7d --- /dev/null +++ b/SpringRESTExample/src/main/resources/mail.properties @@ -0,0 +1,8 @@ +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.username=***** +spring.mail.password=*** + +#mail properties +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true \ No newline at end of file