I want to reduce the validations, maybe using some methods.
I'm delaing with a basic crud, and on the update endpoint, they want to update just one field at once, and giving me a arbitrary number of params, can be 1,2 or 10. and they dont want of course to erase the database if a parameter isnt sent.
#PostMapping("/updateTask")
#ResponseBody
public String updateTask(#RequestBody Task sentTask) {
Task dbTask = null;
try {
dbTask = taskDao.findByIdTask(sentTask.getIdTask());
if(isValid(sentTask.getAuthor())){
dbTask.setAuthor(sentTask.getAuthor());
}
else{
dbTask.setAuthor(dbTask.getAuthor());
}
if(isValid(sentTask.getIdReport())){
dbTask.setIdReport(sentTask.getIdReport());
}
else{
dbTask.setIdReport(dbTask.getIdReport());
}
taskDao.save(dbTask);
} catch (Exception e) {
String response = "{\"data\":
{\"success\":\"false\",\"error\":\"Error updating the task:\"}}";
return response;
}
String response = "{\"data\":{\"success\":\"true\",\"message\":\"Task
updated successfully\",\"Id\":\"" + sentTask.getIdTask() + "\"}}\n";
return response;
}
public boolean isValid(Object data){
if (data == null){
return false;
}
if(data.equals("")){
return false;
}
return true;
}
I want some like this
public void setData(Object sentData, Object dbData){
if (isValid(sentData)){
dbData.setSentData
}
else{
dbData.setDbData
}
}
You can consider using Optional provided in java 8
Optional<String> authorOptional = Optional.ofNullable(sentTask.getAuthor());
if(authorOptional.isPresent()){
dbTask.setAuthor(sentTask.getAuthor());
}else{
dbTask.setAuthor(dbTask.getAuthor());
}
Optional<String> reportOptional = Optional.ofNullable(sentTask.getIdReport());
if(reportOptional.isPresent()){
dbTask.setIdReport(sentTask.getIdReport());
}else{
dbTask.setIdReport(dbTask.getIdReport());
}
OR
dbTask.setAuthor(Optional.ofNullable(sentTask.getAuthor()).isPresent()?sentTask.getAuthor():dbTask.getAuthor());
dbTask.setIdReport(Optional.ofNullable(sentTask.getIdReport()).isPresent()?sentTask.getIdReport():dbTask.getIdReport());
Now you don't need isValid or setData method
I would recommend business logic related changes should be handled service layer
1) First of all the code like this
else {
dbTask.setIdReport(dbTask.getIdReport());
}
doesn't make any sense as long as property value remains unchanged. So we could easily throw it away.
2) Some reduction could be achieved with following approach
Add following methods:
public void updateTask(Task received, Task existing) {
// run validations and copy only valid values
copyValid(received::getAuthor, existing::setAuthor);
copyValid(received::getReportId, existing::setReportId);
taskDao.save(existing);
}
private <T> void copyValid(Supplier<T> getter, Consumer<T> setter) {
Optional.ofNullable(getter.get())
.map(this::validOrNull)
.ifPresent(setter); // setter will be executed only for non-null values
}
private <T> T validOrNull(T data) {
if (data == null) {
return null;
}
if (data instanceof String && data.equals("")) {
return null; // if data is invalid return null
}
// additional checks for other types can be added here
return data; // otherwise return data as is
}
And adjust controller method like this
#PostMapping("/updateTask")
#ResponseBody
public String updateTask(#RequestBody Task sentTask) {
try {
Task dbTask = taskDao.findByIdTask(sentTask.getIdTask());
updateTask(sentTask, dbTask);
} catch (Exception e) {
// omitted
}
}
P.S. As already mentioned in another answer you should extract your business logic to separate service
Related
I have a Java program that calls an external API (RealApi in the code below) and sometimes I want to avoid calling this API and instead return pre-constructed responses (generated by FakeApi).
So, I ended up duplicating this kind of construct in most of my methods:
public Type1 m1(String s) {
try {
Type1 r = FakeApi.m1(s);
if (r != null) {
return r;
}
} catch (Exception e) {
// log error
}
return RealApi.m1(s);
}
What are some options to avoid duplicating this try/catch block everywhere? It's important that if FakeApi throws an exception or returns null, the RealApi must be called.
One option would be encapsulate the error checking behaviour into its own method:
public <T> T fakeOrReal(Supplier<T> fake, Supplier<T> real) {
try {
T r = fake.get();
if (r != null) {
return r;
}
}
catch (Exception e) {
// log error
}
return real.get();
}
You can then just call it with
public Type1 m1(String s) {
return fakeOrReal(() -> FakeApi.m1(s), () -> RealApi.m1(s));
}
This is not as simple as Thomas Preißler's answer but it will help you not repeat any method at all. So if you expand the interface, you have to modify only the concrete classes and not the linker which describes the actual behavior you want.
Create an interface that contains all the methods of RealApi:
interface Api {
Type1 m1(String s);
}
Then a class that does the actual call:
class ConcreteApi implements Api {
public Type1 m1(String s) {
return RealApi.m1(s);
}
}
Then create your FakeApi:
class TotallyFakeApi implements Api {
public Type1 m1(String s) {
return FakeApi.m1(s);
}
}
Now, the tricky part to avoid repeating yourself:
private static Object callImplementation(Api api, Method method, Object[] methodArgs) throws Exception {
Method actualMethod = api.getClass().getMethod(actualMethod.getName(), actualMethod.getParameterTypes());
return actualMethod.invoke(api, methodArgs);
}
Api fakeOrReal(Api fakeApi, Api realApi) {
return (Api) Proxy.newProxyInstance(
FakeApi.class.getClassLoader(),
new Class[]{Api.class},
(proxy, method, methodArgs) -> {
try {
Object r = callImplementation(fakeApi, method, methodArgs);
if (r != null) {
return r;
}
} catch (Exception e) {
// logError(e);
}
return callImplementation(realApi, method, methodArgs);
}
);
}
Get the actual implementation like this:
Api apiToUse = fakeOrReal(new TotallyFakeApi(), new ConcreteApi());
I have an API endpoint where I am receiving JSON files, and I am creating an object from the files. I also have another pre-existing object, and I am trying to check if certain values in the received JSON match certain values in my existing object. If the fields match I will continue to process the file further, if not I will scrap it. My thoughts so far is just to have if statements checking each value, but is there a better way to do this? Or are if statements ok?
Very quick code example of what I mean by just using if statements.
public boolean compareObjects(recievedObject, existingObject) {
if( !(recievedObject.getName().equals(existingObject.getName()))) {
//true
} else if( !(recievedObject.getLocation().equals(existingObject.getLocation())) ) {
return false;
}
// else if ... etc
return true;
}
Note that I am not trying to check if the received file has all the required JSON fields, just that a few particular fields have certain values.
Edit:
The JSON will be a very flat structure e.g
{
"name": "name",
"location": "location",
...
}
So my object will be pretty basic
public class recievedObject {
String location;
String name;
public String getLocation() {
return location;
}
public String getName() {
return name;
}
}
What you can do to avoid lot of it-else statements is to create some validation abstraction.
interface Validator<A, B> {
boolean validate(A receivedObject, B existingObject);
}
Then for each if create new implementation of Validator.
class NameValidator implements Validator<Expeceted, Received> {
#Override
public boolean validate(Expeceted receivedObject, Received existingObject) {
return existingObject.getName().equals(receivedObject.getName());
}
}
class LocationValidator implements Validator<Expeceted, Received> {
#Override
public boolean validate(Expeceted receivedObject, Received existingObject) {
return existingObject.getLocation().equals(receivedObject.getLocation());
}
}
You can create list of such a validators
List<Validator<Expeceted, Received>> validators = Arrays.asList(
new NameValidator(),
new LocationValidator()
);
And finally your compare method could simply iterate through all validators.
public boolean compareObjects(Received recievedObject, Expeceted expecetedObject) {
for (Validator<Expeceted, Received> validation : validators) {
if (! validation.validate(expecetedObject, recievedObject)) {
return false;
}
}
return true;
}
This way you can simply add new validators later and keep compare method untouched.
define a method similar to 'equal' for your class and at the endpoint check the existingObject.check(receivedObject), add import java.util.Objects to your class
public boolean check(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RecievedObject receivedObject=(RecievedObject) o;
//add based on the logic you want
return Objects.equals(location, receivedObject.location) &&
Objects.equals(name, receivedObject.name);
}
I am working on a Spring-MVC application in which I have certain methods which do task of creating previews of files like doc, docx, ppt, etc. Now, the entry point for all these methods is a single method. I am using multiple technologies like docx4j, apache-poi, etc.
Even after a lot of tests, sometimes the conversion fails, which is not a problem, but the request from front-end is not finished and the tab eventually dies. WHat I would like to do is to give a timeout for the entry-point method, so if the conversion doesn't succeed in 20 seconds, then the conversion process is halted.
IS there anything similar I can do in Spring-MVC.
Code :
#Service
#Transactional
public class GroupAttachmentsServiceImpl implements GroupAttachmentsService {
#Override
public boolean addAttachment(byte[] bytes, String fileName){
// Attachment to file-system persistence code
try {
attachment.setImageThumbnail(createFilePreviewForFiles(fileName, bytes));
} catch (Exception ignored) {
}
}
// Below is entry-point method for which I would like to set some timeout
#Override
public String createFilePreviewForFiles(String fileName, byte[] fileBytes) {
try {
if (!(fileBytes == null)) {
String targetLocation = zipLocation + String.valueOf(new BigInteger(130, random).toString(32));
FileUtils.writeByteArrayToFile(new File(targetLocation), fileBytes);
String extension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
if (extension.toLowerCase().equals("pdf")) {
return (createPdfPreview(targetLocation));
}
if ((extension.toLowerCase().equals("pptx"))) {
return (createPPtxPreview(targetLocation));
}
if (extension.toLowerCase().equals("ppt")) {
// return createPptPreview(targetLocation);
return null;
}
if (extension.toLowerCase().equals("doc")) {
return createDocToPDfAndThenToImage(targetLocation);
// return null;
}
if ((extension.toLowerCase().equals("docx"))) {
return (createDocxToPdfAndThenToImage(targetLocation));
}
if (extension.toLowerCase().equals("xls")) {
return (convertXlsToPDfAndToImage(targetLocation));
}
if (extension.toLowerCase().equals("xlsx")) {
return (convertXlsxToPdfAndToImage(targetLocation));
}
if (extension.toLowerCase().equals("png")) {
return createThumbNailWithoutPathAndReturnImage(fileBytes);
}
if (extension.toLowerCase().equals("jpg")) {
return createThumbNailWithoutPathAndReturnImage(fileBytes);
}
if (extension.toLowerCase().equals("jpeg")) {
return createThumbNailWithoutPathAndReturnImage(fileBytes);
}
if (extension.toLowerCase().equals("mp4")) {
return createPreviewForVideos(targetLocation);
}
}
} catch (Exception ignored) {
return "";
}
return null;
}
One of the ways to implement your requirement is to call your method asynchronously. The method will need to return Future or CompletableFuture (if you are using Java 8).
So you will have a method annotated with #Async (you need to enable async with #EnableAsync in your config):
#Async
CompletableFuture<Void> createDocxToPdfAndThenToImage(String targetLocation)
and your call could be (there are a lot of other options with CompletableFuture):
CompletableFuture<Void> future= createDocxToPdfAndThenToImage(targetLocation);
future.get( 10, SECONDS)
This will throw a TimeoutException if method takes longer than the timeout value.
Since in your case you need to return String, you can do
#Async
CompletableFuture<String> createDocxToPdfAndThenToImage(String targetLocation) ....
CompletableFuture<String> future=createDocxToPdfAndThenToImage(targetLocation);
return future.get( 10, SECONDS);
I had writtern a piece of code:In this code we are calling validitem function to check if passes param is some valid item or not.If it is valid item,i want to return that item else i want to abort processing stating its not valid item.Please suggest solution for the initial code writtern below, on how to handle situation where we dont want to return anything.And if we return null,then how and where to have exception catching?
public void constructdata(){
String foo = validitem(param);
//code to avoid processing if null is returned
}
public validitem(String item){
if(item.equals("Apple"){
return item;
}
if(item.equals("Ball"){
return item;}
return null;
}
If you want validitem() still to return null and if so, throw an exception:
String foo = validitem(param);
if ( foo != null ) {
container.putField(key, foo);
} else {
throw new Exception();
}
If you want to make validitem() only return a valid value and throw an exception otherwise:
public void constructdata(){
try {
container.putField(key, validitem(param));
} catch (Exception e) {
// Handle exception
}
}
public validitem(String item) throws Exception {
if (item.equals("Apple") {
return item;
}
if (item.equals("Ball") {
return item;
}
throw new Exception("Error message");
}
You should use a more specific exception than Exception(). I've only used it to demonstrate the logic.
Avoid returning null when possible because it can lead to bugs, insted you can return a Java8 Optional. If you are not using java8 there is an equivalent in the guava library. Returning an Optional forces the client to think about how to handle the case where nothing is returned.
public void method() {
returnValue = optional.absent();
try {
GetDate dateResponse = client.call();
if (dateResponse != null) {
myDate = dateResponse.getDate();
if (myDate != null) {
returnValue = convertDateToAnotherDate() //actual function
if (!returnValue.isPresent()) {
LOG.error("Missing required fields");
}
} else {
LOG.error("No myDate");
}
} else {
LOG.error("Service returned no dateResponse");
}
} catch (CustomException exception) {
LOG.error("Custom service failed");
}
return retVal;
}
You need the if's, but you can rearrange to make it a lot clearer an logically organised, following principles if:
fail early
minimise nesting
don't declare variables until needed/minimise their scope
After applying the above tenets:
public void method() {
try {
GetDate dateResponse = client.call();
if (dateResponse == null) {
LOG.error("Service returned no dateResponse");
return optional.absent();
}
myDate = dateResponse.getDate();
if (myDate == null) {
LOG.error("No myDate");
return optional.absent();
}
returnValue = convertDateToAnotherDate() //actual function
if (returnValue.isPresent())
return returnValue;
LOG.error("Missing required fields");
} catch (CustomException exception) {
LOG.error("Custom service failed");
}
return optional.absent();
}
Notice how now the tests are all positive tests (using == rather than !=, which our tiny brains can comprehend better). The indentation (nesting) is reduced, so readability is up. The returnValue variable is only needed in the middle of the code too, so no need to declare it early.
You can combine them, as in:
if(dateResponse!=null && dateResponse.getDate() !=null)
Given the way you're reacting to what could happen if those values returned null, perhaps it's a better idea to throw exceptions from the calling methods, and log the exception out. You can also change your dateResponse to an Optional<GetDate>, and behave appropriately when it's absent.
Consider:
public Optional<Date> method() {
Optional<Date> returnValue = Optional.absent();
Optional<GetDate> dateResponse = client.call();
if (dateResponse.isPresent()) {
try {
Date myDate = dateResponse.getDate();
returnValue = convertDateToAnotherDate(date); //actual function
} catch (IllegalStateException e) {
LOG.error(e);
}
} else {
LOG.error("Service returned no dateResponse");
}
return returnValue;
}
I'm assuming that client will return an Optional<Date>, and if it's present, we'll behave normally.
I execute normal business logic, as if the threat of null isn't possible.
If an error occurs (which I consider to be an IllegalStateException), I expect the methods that I call to throw it.
I log the exception that occurs, and provide a meaningful message when the exception is constructed.
An example structure of getDate could read like this:
public Date getDate() {
if(date == null) {
throw new IllegalStateException("No date present");
}
return date;
}
...and now we're down to one if.
I genuinely don't know what types your variables are (I mean, I really don't - I asked if this would compile and I have my doubts), so this is about as far as I can go with the suggestion.