InInterceptors annotation does not work in WL - maven

I have the code below:
#WebService(serviceName = "ServicioHTTP")
#Stateless
#InInterceptors(interceptors = "ws.BasicAuthAuthorizationInterceptor")
public class ServicioHTTP {
#WebMethod(operationName = "serviceAuthHTTP")
public String serviceAuthHTTP(#WebParam(name = "input") String input) {
return input;
}
}
What I'm trying to do is an authentication using HTTP, but it does not work the service always return the input, the InInterceptors is not firing and I dont kno why, I'm deploying in a Weblogic Server
Any idea?
Thanks

Related

Internationalization file in SpringBoot Application

I have Springboot Application for Rest Service. And rest services are documented using Swagger/ springfox annotations, and I want use i18n file properties for internationalization feautures in my App....
as I do in the controller I dynamically retrieve the property of the 'message' in annotation #ApiResponses?
#ApiResponses(value = {
#ApiResponse(code = HttpURLConnection.HTTP_OK, message = "Successful login to platform"),
#ApiResponse(code = HttpURLConnection.HTTP_UNAUTHORIZED, message = "Unauthorized request to platform")
})
Thanks
Seems as of now (2018-08-08) it's currently not supported. You can take a look at the issue here: link
Had similar issue.
First if you look at current Springfox (Version 2.9.2) documentation, they supports following annotations out of the box:
#ApiParam#value()
#ApiImplicitParam#value()
#ApiModelProperty#value()
#ApiOperation#value()
#ApiOperation#notes()
#RequestParam#defaultValue()
#RequestHeader#defaultValue()
It took me some time to get dynamic #ApiResponse message, so I will post a reply to help others.
Solution is based on https://github.com/springfox/springfox/issues/1180
First I added following to my swagger configuration file:
#Bean
public TranslationOperationBuilderPlugin translationPlugin() {
return new TranslationOperationBuilderPlugin();
}
//important to keep this LOWEST_PRECEDENCE!!!
#Order(Ordered.LOWEST_PRECEDENCE)
public static class TranslationOperationBuilderPlugin implements OperationBuilderPlugin {
#Autowired
protected Environment env;
#Override
public boolean supports(DocumentationType delimiter) {
return true;
}
#Override
public void apply(OperationContext context) {
Set<ResponseMessage> messages = context.operationBuilder().build().getResponseMessages();
Set<ResponseMessage> translated = new HashSet<>();
for (ResponseMessage untranslated : messages) {
String translation = env.getProperty(untranslated.getMessage());
translated.add(new ResponseMessage(untranslated.getCode(),
translation,
untranslated.getResponseModel(),
untranslated.getHeaders(),
untranslated.getVendorExtensions()
));
}
context.operationBuilder().responseMessages(translated);
}
}
On controller class I add #PropertySource(value= "classpath:swagger.properties", encoding="UTF-8")
This file is located inside usual resources directory and contains code_400=my message
And on method in controller
#ApiResponses(value = {
#ApiResponse(code = 400, message = ResponseKeys.MESSAGE_400)
})
And finally ResponseKeys contains:
public class ResponseKeys {
/* 4xx messages */
public static final String MESSAGE_400 = "code_400";
}

Feign client and Spring retry

I have a restful service calling an external service using Spring Cloud Feign client
#FeignClient(name = "external-service", configuration = FeignClientConfig.class)
public interface ServiceClient {
#RequestMapping(value = "/test/payments", method = RequestMethod.POST)
public void addPayment(#Valid #RequestBody AddPaymentRequest addPaymentRequest);
#RequestMapping(value = "/test/payments/{paymentId}", method = RequestMethod.PUT)
public ChangePaymentStatusResponse updatePaymentStatus(#PathVariable("paymentId") String paymentId,
#Valid #RequestBody PaymentStatusUpdateRequest paymentStatusUpdateRequest);
}
I noticed the following failure 3-4 times in the last 3 months in my log file:
json.ERROR_RESPONSE_BODY:Connection refused executing POST
http://external-service/external/payments json.message:Send Payment
Add Payment Failure For other reason: {ERROR_RESPONSE_BODY=Connection
refused executing POST http://external-service/external/payments,
EVENT=ADD_PAYMENT_FAILURE, TRANSACTION_ID=XXXXXXX} {}
json.EVENT:ADD_PAYMENT_FAILURE
json.stack_trace:feign.RetryableException: Connection refused
executing POST http://external-service/external/payments at
feign.FeignException.errorExecuting(FeignException.java:67) at
feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
at
feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at
feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
Is it possible to add Spring Retry on a Feign client.
What I wanted to annotate the addPayment operation with
#Retryable(value = {feign.RetryableException.class }, maxAttempts = 3, backoff = #Backoff(delay = 2000, multiplier=2))
But this is not possible, what other options do I have?
You can add a Retryer in the FeignClientConfig
#Configuration
public class FeignClientConfig {
#Bean
public Retryer retryer() {
return new Custom();
}
}
class Custom implements Retryer {
private final int maxAttempts;
private final long backoff;
int attempt;
public Custom() {
this(2000, 3);
}
public Custom(long backoff, int maxAttempts) {
this.backoff = backoff;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
try {
Thread.sleep(backoff);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
#Override
public Retryer clone() {
return new Custom(backoff, maxAttempts);
}
}
Updated with sample Retryer example config based on the Retryer.Default.
If you are using ribbon you can set properties, you can use below properties for retry:
myapp.ribbon.MaxAutoRetries=5
myapp.ribbon.MaxAutoRetriesNextServer=5
myapp.ribbon.OkToRetryOnAllOperations=true
Note: "myapp" is your service id.
Checkout this Github implementation for working example
Just new a contructor Default
#Configuration
public class FeignClientConfig {
#Bean
public Retryer retryer() {
return new Retryer.Default(100, 2000, 3);
}
}
Adding this if it can help someone. I was getting connection reset using feign, as some unknown process was running on that port.
Try changing the port. Refer this to find the process running on a port
I prepared a blog post about using Spring Retry with Feign Client methods. You may consider checking the Post. All steps have been explained in the post.
This is my config. Test OK in spring boot 2.2.0.RELEASE
spring cloud Hoxton.M3.
feign.hystrix.enabled=true
MY-SPRING-API.ribbon.MaxAutoRetries=2
MY-SPRING-API.ribbon.MaxAutoRetriesNextServer=2
MY-SPRING-API.ribbon.OkToRetryOnAllOperations=true
MY-SPRING-API.ribbon.retryableStatusCodes=404,500
feign.client.config.PythonPatentClient.connectTimeout=500
feign.client.config.PythonPatentClient.readTimeout=500
hystrix.command.PythonPatentClient#timeTest(String).execution.isolation.thread.timeoutInMilliseconds=5000
java code is :
#FeignClient(name = "MY-SPRING-API",configuration = {PythonPatentConfig.class},fallbackFactory = FallBack.class)
public interface PythonPatentClient
#RequestLine("GET /test?q={q}")
void timeTest(#Param("appNo") String q);
Controller is :
#RequestMapping(value = "/test",method = {RequestMethod.POST,RequestMethod.GET})
public Object test() throws InterruptedException {
log.info("========important print enter test========");
TimeUnit.SECONDS.sleep(10L);
pom.xml additon add:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
optional:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
#EnableRetry
#SpringBootApplication
public class ApiApplication
this is document :
https://docs.spring.io/spring-cloud-netflix/docs/2.2.10.RELEASE/reference/html/#retrying-failed-requests
https://github.com/spring-projects/spring-retry
https://github.com/spring-cloud/spring-cloud-netflix/
I resolved that by creating a wrapper on top of ServiceClient
#Configuration
public class ServiceClient {
#Autowired
ServiceFeignClient serviceFeignClient;
#Retryable(value = { ClientReprocessException.class }, maxAttemptsExpression = "#{${retryMaxAttempts}}", backoff = #Backoff(delayExpression = "#{${retryDelayTime}}"))
public void addPayment( AddPaymentRequest addPaymentRequest){
return serviceFeignClient.addPayment(addPaymentRequest);
}
}

How to use encrypted store-uri in Spring ImapIdleChannelAdapter

Sample spring configuration is as below.
<int-mail:imap-idle-channel-adapter id="mailAdapter"
store-uri="imaps://${"username"}:${"password"}#imap-server:993/INBOX"
java-mail-properties="javaMailProperties"
channel="emails"
should-delete-messages="false"
should-mark-messages-as-read="true">
</int-mail:imap-idle-channel-adapter>
I wish to keep the password field encrypted in properties file and decrypt it in the code. I am not sure on how to set mailReceiver property of ImapIdleChannelAdapter to my custom version of ImapMailReceiver.
Please let me know if there is any way to do this.
All of my configurations are in XML as described above.
Above solution of adding the defifnation did not work may be I am doing something wrong. Then I tried using XML + Java configuration, as below.
#Configuration
public class EmailConfiguration {
#Bean
public ImapIdleChannelAdapter customAdapter() {
ImapIdleChannelAdapter adapter = new ImapIdleChannelAdapter(mailReceiver());
adapter.setOutputChannel(outputChannel());
adapter.setErrorChannel(errorChannel());
adapter.setAutoStartup(true);
adapter.setShouldReconnectAutomatically(true);
adapter.setTaskScheduler(taskScheduler());
return adapter;
}
#Bean
public TaskImapMailReceiver mailReceiver() {
TaskImapMailReceiver mailReceiver = new TaskImapMailReceiver("imaps://[username]:[password]#imap.googlemail.com:993/inbox");
mailReceiver.setShouldDeleteMessages(false);
mailReceiver.setShouldMarkMessagesAsRead(true);
//mailReceiver.setJavaMailProperties(javaMailProperties());
mailReceiver.setMaxFetchSize(Integer.MAX_VALUE);
return mailReceiver;
}
}
Also created empty errorChannel,outputChannel etc. I observed that Spring creates two instances one with xml config and other with java #Configuration. Where it was expected to use only java configuration. If I remove the xml config tag
then it provides sigle imap instance with my mailReceiver but runs only once does not go periodic. also does not show IMAPS logs.
Just wondering if I need to do so much to encrypt the password. Is somthing wrong with my approach.
Use Java configuration instead of XML...
#Configuration
public class MyConfigClass {
#Bean
public MyMailReceiver receiver() {
...
}
#Bean
public ImapIdleChannelAdapter adapter() {
ImapIdleChannelAdapter adapter = new ImapIdleChannelAdapter(receiver());
...
return adapter;
}
}
If you are using XML for everything else, simply add this class as a <bean/> to your XML.
EDIT
Here's an example that works fine for me...
#SpringBootApplication
public class So42298254Application {
public static void main(String[] args) {
SpringApplication.run(So42298254Application.class, args);
}
#Bean
public TestMailServer.ImapServer imapServer() {
return TestMailServer.imap(0);
}
#Bean
public ImapMailReceiver receiver() {
ImapMailReceiver imapMailReceiver = new ImapMailReceiver(imapUrl("user", "pw"));
imapMailReceiver.setHeaderMapper(new DefaultMailHeaderMapper()); // converts the MimeMessage to a String
imapMailReceiver.setUserFlag("testSIUserFlag"); // needed by the SI test server
Properties javaMailProperties = new Properties();
javaMailProperties.put("mail.debug", "true");
imapMailReceiver.setJavaMailProperties(javaMailProperties);
return imapMailReceiver;
}
private String imapUrl(String user, String pw) {
return "imap://"
+ user + ":" + pw
+ "#localhost:" + imapServer().getPort() + "/INBOX";
}
#Bean
public ImapIdleChannelAdapter adapter() {
ImapIdleChannelAdapter adapter = new ImapIdleChannelAdapter(receiver());
adapter.setOutputChannelName("handleMail");
return adapter;
}
#ServiceActivator(inputChannel = "handleMail")
public void handle(String mail, #Header(MailHeaders.FROM) Object from) {
System.out.println(mail + " from:" + from);
imapServer().resetServer(); // so we'll get the email again
}
}
My intention was to use encrypted passwords in properties files.
So I changed my approach of getting into email receiving classes. I added inherited PropertyPlaceholderConfigurer and implemented method convertPropertyValue() as below.
public class EncryptationAwarePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private static final Logger logger = LoggerFactory.getLogger(EncryptationAwarePropertyPlaceholderConfigurer.class);
#Override
protected String convertPropertyValue(String originalValue) {
if (originalValue.contains("{<ENC>}") && originalValue.contains("{</ENC>}")) {
String encryptedTaggedValue = originalValue.substring(originalValue.indexOf("{<ENC>}"), originalValue.indexOf("{</ENC>}") + 8);
String encryptedValue = originalValue.substring(originalValue.indexOf("{<ENC>}") + 7, originalValue.indexOf("{</ENC>}"));
try {
String decryptedValue = EncrypDecriptUtil.decrypt(encryptedValue);//EncrypDecriptUtil is my class for encription and decryption
originalValue = originalValue.replace(encryptedTaggedValue, decryptedValue);
} catch (GeneralSecurityException e) {
logger.error("failed to decrypt property returning original value as in properties file.", e);
}
}
return originalValue;
}
}
And changed properties file to enclose encrypted value in custuom ENC tag
as
mail.imap.task.url=imap://username:{<ENC>}encryptedPassword{</ENC>}#imap.googlemail.com:993/inbox

How to do URL Rewrite in Zuul Proxy?

One of the request that comes to my Zuul Filter is of URI /hello/World which i want to redirect to /myapp/test. This /myapp/test is a service that is registered in Eureka.
zuul:
routes:
xyz:
path: /hello/World
url: http://localhost:1234/myapp/test
stripPrefix: true
When i try the above configuration, the incoming URI is suffixed to the configured URL like http://localhost:1234/myapp/test/World . Few of the links which i came across seem to be stating that URL Rewrite feature is not yet available in Zuul.
Is there any other way this can be done at the Zuul Layer ?
Note: At this point of time, i cannot do this reverse proxying in the Webserver or any other layer since, my Zuul filter is the one that is receiving the request directly.
Using #Adelin solution, with little improvements
Use 'url' property as path to prepend for customizing the Url rewriting (I have disabled Eureka in my example) :
ribbon.eureka.enabled=false
zuul.routes.route1.path=/route1/**
zuul.routes.route1.serviceId=service1
zuul.routes.route1.url=/path/to/prepend
service1.ribbon.listOfServers=http://server1
Then implement the following filter :
/**
* Fixing missing URL rewriting when using ribbon
*/
#Component
public class CustomPathZuulFilter extends ZuulFilter {
#Autowired
private ZuulProperties zuulProperties;
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
#Override
public boolean shouldFilter() {
// override PreDecorationFilter only if executed previously successfully
return RequestContext.getCurrentContext().getFilterExecutionSummary().toString()
.contains("PreDecorationFilter[SUCCESS]");
}
#Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
if (context.get(FilterConstants.SERVICE_ID_KEY) == null || context.getRouteHost() != null) {
// not a Ribbon route
return null;
}
// get current ZuulRoute
final String proxy = (String) context.get(FilterConstants.PROXY_KEY);
final ZuulRoute zuulRoute = this.zuulProperties.getRoutes().get(proxy);
// patch URL by prefixing it with zuulRoute.url
final Object originalRequestPath = context.get(FilterConstants.REQUEST_URI_KEY);
final String modifiedRequestPath = zuulRoute.getUrl() + originalRequestPath;
context.put(FilterConstants.REQUEST_URI_KEY, modifiedRequestPath);
// patch serviceId because :
// - has been set to route.location in PreDecorationFilter
// - route.location has been set to zuulRoute.location in SimpleRouteLocator
// - zuulRoute.location return zuulRoute.url if set
context.set(FilterConstants.SERVICE_ID_KEY, zuulRoute.getServiceId());
return null;
}
}
Now calls to /route1 will be proxified to http://server1/path/to/prepend
This solution is also compatible with co-existing routes not using Ribbon.
Example of a co-existing route not using Ribbon :
zuul.routes.route2.path=/route2/**
zuul.routes.route2.url=http://server2/some/path
Calls to /route2 will be proxified to http://server2/some/path by SimpleHostRoutingFilter (if not disabled)
Here is a posted solution in the link by #Vikash
#Component
public class CustomPathZuulFilter extends ZuulFilter
{
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return PreDecorationFilter.FILTER_ORDER + 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
Object originalRequestPath = context.get(REQUEST_URI_KEY);
String modifiedRequestPath = "/api/microservicePath" + originalRequestPath;
context.put(REQUEST_URI_KEY, modifiedRequestPath);
return null;
}
}
Have you tried creating a preFilter or even a routeFilter ?
That way you can intercept the request, and change the routing.
See Zuul Filters

#MessageMapping with placeholders

I am working with Spring-websocket and I have the following problem:
I am trying to put a placeholder inside a #MessageMapping annotation in order to get the url from properties. It works with #RequestMapping but not with #MessageMapping.
If I use this placeholder, the URL is null. Any idea or suggestion?
Example:
#RequestMapping(value= "${myProperty}")
#MessageMapping("${myProperty}")
Rossen Stoyanchev added placeholder support for #MessageMapping and #SubscribeMapping methods.
See Jira issue: https://jira.spring.io/browse/SPR-13271
Spring allows you to use property placeholders in #RequestMapping, but not in #MessageMapping. This is 'cause the MessageHandler. So, we need to override the default MessageHandler to do this.
WebSocketAnnotationMethodMessageHandler does not support placeholders and you need add this support yourself.
For simplicity I just created another WebSocketAnnotationMethodMessageHandler class in my project at the same package of the original, org.springframework.web.socket.messaging, and override getMappingForMethod method from SimpAnnotationMethodMessageHandler with same content, changing only how SimpMessageMappingInfo is contructed using this with this methods (private in WebSocketAnnotationMethodMessageHandler):
private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
These methods now will resolve value considering properties (calling resolveAnnotationValues method), so we need use something like this:
private String[] resolveAnnotationValues(final String[] destinationNames) {
final int length = destinationNames.length;
final String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = this.resolveAnnotationValue(destinationNames[i]);
}
return result;
}
private String resolveAnnotationValue(final String name) {
if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
return name;
}
final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();
final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return name;
}
final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
return result != null ? result.toString() : name;
}
You still need to define a PropertySourcesPlaceholderConfigurer bean in your configuration.
If you are using XML based configuration, include something like this:
<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />
If you are using Java based configuration, you can try in this way:
#Configuration
#PropertySources(value = #PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Obs.: in this case, url-mapping-config.properties file are in a gradle/maven project in src\main\resources\META-INF\spring folder and content look like this:
myPropertyWS=urlvaluews
This is my sample controller:
#Controller
public class WebSocketController {
#SendTo("/topic/test")
#MessageMapping("${myPropertyWS}")
public String test() throws Exception {
Thread.sleep(4000); // simulated delay
return "OK";
}
}
With default MessageHandler startup log will print something like this:
INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
And with our MessageHandler now print this:
INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
See in this gist the full WebSocketAnnotationMethodMessageHandler implementation.
EDIT: this solution resolves the problem for versions before 4.2 GA. For more information, see this jira.
Update :
Now I understood what you mean, but I think that is not possible(yet).
Documentation does not mention anything related to Path mapping URIs.
Old answer
Use
#MessageMapping("/handler/{myProperty}")
instead of
#MessageMapping("/handler/${myProperty}")
And use it like this:
#MessageMapping("/myHandler/{username}")
public void handleTextMessage(#DestinationVariable String username,Message message) {
//do something
}
#MessageMapping("/chat/{roomId}")
public Message handleMessages(#DestinationVariable("roomId") String roomId, #Payload Message message, Traveler traveler) throws Exception {
System.out.println("Message received for room: " + roomId);
System.out.println("User: " + traveler.toString());
// store message in database
message.setAuthor(traveler);
message.setChatRoomId(Integer.parseInt(roomId));
int id = MessageRepository.getInstance().save(message);
message.setId(id);
return message;
}

Resources