Recently I was creating small distributed application called ClientBackend just for interview purpose. Solution consists of thee microservices :
- Client (received request from API Gateway and sends it to Backend)
- Backend (performs operation defined in request)
- Discovery (Spring Eureka)
- API Gateway (Spring Zuul)
- Java 8
- Spring Boot
- Hystrix
- Eureka
- Feign
- Zuul
- TypeOf
Sometimes asserting custom exception is difficult. Sometimes it's not, for instance when you are using AssertJ, it's very quite easy to achieve. In this post I would like to highlight how to achieve that using Hamcrest matcher.
Below you might find a BaseExceptionMatcher which can be easily extended for our needs. Let's assume that we have an exception with four properties like errorType, errorCode, status and description.
import com.my.package.ErrorCode; import com.my.package.ErrorType; import com.my.package.base.BaseException; import com.my.package.MessageType; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import javax.ws.rs.core.Response; public abstract class BaseExceptionMatcher extends TypeSafeMatcher<BaseException> { protected ErrorType expectedErrorType; protected ErrorCode expectedErrorCode; protected Response.Status expectedStatus; protected String expectedDescription; public BaseExceptionMatcher(ErrorCode expectedErrorCode, Response.Status expectedStatus, ErrorType expectedErrorType, String expectedDescription) { this.expectedErrorCode = expectedErrorCode; this.expectedStatus = expectedStatus; this.expectedErrorType = expectedErrorType; this.expectedDescription = expectedDescription; } @Override protected boolean matchesSafely(BaseException item) { return item.getStatus().equals(expectedStatus) && item.getError().stream().allMatch(p -> p.getType().equals(expectedErrorType.name()) && p.getCode().equals(expectedErrorCode.name())) && item.getError().stream().allMatch(p -> p.getDescription().contains(expectedDescription)); } @Override public void describeTo(Description description) { description.appendText("Response status : ") .appendValue(expectedStatus.getReasonPhrase() + " (" + expectedStatus.getStatusCode() + ")") .appendText(" | ErrorType : ").appendValue(expectedErrorType.name()) .appendText(" | ErrorCode : ").appendValue(expectedErrorCode.name()) .appendText(" | Description : ").appendValue(expectedDescription); } @Override protected void describeMismatchSafely(BaseException item, Description mismatchDescription) { mismatchDescription.appendText("Exception contains response status : ") .appendValue(item.getStatus().getReasonPhrase() + "(" + item.getStatus().getStatusCode() + ")") .appendText(" | ErrorType : ").appendValue(getMessageType(item).getType()) .appendText(" | ErrorCode : ").appendValue(getMessageType(item).getCode()) .appendText(" | Description : ").appendValue(getMessageType(item).getDescription()); } private MessageType getMessageType(BaseException item) { return item.getError().stream().findFirst().orElseGet(() -> { MessageType messageType = new MessageType(); messageType.setType(ErrorType.APPLICATION.name()); messageType.setCode(ErrorCode.INTERNAL_SERVER_ERROR.name()); messageType.setDescription("Matcher Error"); return messageType; }); } }
import my.package.ErrorCode; import my.package.ErrorType; import my.package.BaseExceptionMatcher; import javax.ws.rs.core.Response; public class MyExceptionMatcher extends BaseExceptionMatcher { public MyExceptionMatcher(ErrorType expectedErrorType, ErrorCode expectedErrorCode, Response.Status expectedStatus, String expectedDescription) { super(expectedErrorCode, expectedStatus, expectedErrorType, expectedDescription); } }
@Rule public ExpectedException expectedException = ExpectedException.none();
public void testFailure() throws MyException { expectedException.expect(new MyExceptionMatcher(ErrorType.APPLICATION, ErrorCode.DOCUMENT_NOT_FOUND, Response.Status.NOT_FOUND, "Document not found")); //do smth to fail }
Last time I was describing how to enable Tomcat for projects maintained by Gradle in post with title called "IntelliJ with Tomcat and Gradle using Gretty plugin". The thing that I found recently even a better way to achieve that! Moreover this way is less invasive in terms of that you do not need to append any configuration to your gradle scripts! This better way is known as a Smart Tomcat! It is a plugin for IntelliJ which allow you to configure literally everything around your app if it should be run on Tomcat.
Smart Tomcat - IntelliJ IDEA Plugins
Last time I was looking for a way to convert a Map into a Map of List. In otherwise direction, I mean backward from value up to key. Below you might find an solution for that problem using of course main Java 8 components like stream, filter, grouping and mapping.
Map<String, String> input = new HashMap<>(); input.put("SHARK", null); input.put("DOG", "HOME"); input.put("CAT", "HOME"); input.put("TURTLE", "HOME"); input.put("RABBIT", "HOME"); input.put("LION", "NOT_HOME"); input.put("HIPPO", "NOT_HOME"); input.put("TIGER", "NOT_HOME"); input.put("ZEBRA", "NOT_HOME"); Map<String, List<String>> result = input.entrySet().stream() .filter(i -> i.getValue() != null) .collect(Collectors.groupingBy(i -> i.getValue(), Collectors.mapping(i -> i.getKey(), Collectors.toList())));
Results
Key : HOME with List of DOG, CAT, TURTLE and RABBIT
Key : NOT_HOME with List of LION, HIPPO, TIGER, ZEBRA
This time I would like to describe briefly how to enable Tomcat in debug & class reload mode on IntelliJ when project is built using Gradle using this time Gretty plugin [1].
1) Add below line to build.gradle after applied war plugin
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'
2) At the end of build.gradle add also below statement which allows us to reload classes in container in the fly after change made by you in IntelliJ
gretty {
managedClassReload = true
}
3) Add Remote item in IntelliJ [3]
4) Run project using below command line
gradle tomcatStartDebug --info
5) Pickup Remote item in IntelliJ and run it in debug mode
6) Place breakpoints and run the code to catch them
Reference :
[1] https://github.com/akhikhl/gretty
[2] Hot deployment in Gretty
[3] Adding Remote item in IntelliJ
Oracle - duplicate keys found for novalidate unique constraint
by GarciaPL on Tuesday, 10 January 2017
I was trying recently to add unique constraint on few fields on Oracle. At the beginning I thought that it might be a easy task to do, but at the end some workaround was needed. So, in case of error you might get below message which indicates that Oracle found some duplicate records in your table, even though you specify NOVALIDATE flag which allows you to do not check existing data against that constraint.
ALTER TABLE MY_TABLE ADD CONSTRAINT MY_UNIQUE_CONSTRAINT UNIQUE (ID, CUSTOM_ID, VALUE, UNITS, AMOUNT, ITEM_ID, DATE) NOVALIDATE Error report - SQL Error: ORA-02299: cannot validate (DB.MY_UNIQUE_CONSTRAINT) - duplicate keys found 02299. 00000 - "cannot validate (%s.%s) - duplicate keys found" *Cause: an alter table validating constraint failed because the table has duplicate key values. *Action: Obvious
That's why we need to define simple INDEX on fields which will be used during setting UNIQUE CONSTRAINT before setting our UNIQUE CONSTRAINT.
CREATE INDEX INDEX_UNIQUE_CONSTRAINT ON MY_TABLE (ID, CUSTOM_ID, VALUE, UNITS, AMOUNT, ITEM_ID, DATE); ALTER TABLE MY_TABLE ADD CONSTRAINT MY_UNIQUE_CONSTRAINT UNIQUE (ID, CUSTOM_ID, VALUE, UNITS, AMOUNT, ITEM_ID, DATE) NOVALIDATE;
References : [1] ENABLE NOVALIDATE validating existing data - Ask Tom [2] NOVALIDATE Constraints – No really - richardfoote
As you can see Spring Integration posts are going to be continued! This time I would like to share with you small hint of code which will help you to route your message based on existing or not key Signature in your headers.
<int:router id="headerRouter" expression="headers.containsKey('Signature')" default-output-channel="processFurther"> <int:mapping value="false" channel="drop"/> <int:mapping value="true" channel="processFurther"/> </int:router>
References : [1] Spring Docs - Message Routing
Do you use inbound or outbound http gateway in Spring Integration and you was thinking about caching some requests ? There is a solution for that! It's can be achieved with <request-handler-advice-chain> and Spring Cache Advice.
<int-http:outbound-gateway> <int-http:request-handler-advice-chain> <cache:advice> <cache:caching cache="cacheKey"> <cache:cacheable method="handle*Message" key="#a0.payload.id"/> </cache:caching> </cache:advice> </int-http:request-handler-advice-chain> </int-http:outbound-gateway>
You might see above that handle*Message will be considered by Spring Cache Advice to invoke cache functionality. To be more specific we are thinking about handleRequestMessage method and it's parameters defined as a part of HttpRequestExecutingMessageHandler which will be considered as a key - id in our case. Moreover you need to define explicitly cacheManager bean and define key for cache entry (cacheKey in our case) using for instance EhCache.
References : [1] Spring Integration - Cache for inbound/outbound gateway