Google Remote API 404 on Springboot - spring

I'm trying to connect a Spring Boot app running locally/not in Google app engine when deployed to my Google DataStore. I am using Objectify and Google Remote API as my understanding is that Objectify will only work if deployed in App engine(?). The problem is that the Google Remote API is throwing 404 when I try and communicate with the DataStore. I think I might have my config wrong in spring boot as the instructions on the google docs talk about setting up config in web.xml which I don't use on my JAR based spring boot app. I have created a SpringBootServletInitializer class to try and register the servlet (code at the bottom). I'm coming at this from an AWS perspective and so far I've found Google cloud to be a nightmare! Connecting to DynamoDB is so much simpler and I feel I must be missing something!
import com.google.apphosting.utils.remoteapi.RemoteApiServlet;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Thomas on 01/07/2017.
*/
#Configuration
public class GoogleRemoteApiConfig extends SpringBootServletInitializer {
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(GoogleRemoteApiConfig.class, "/remote_api");
}
#Bean
public ServletRegistrationBean remoteApiRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(new RemoteApiServlet(), "/remote_api");
Map<String,String> params = new HashMap<String,String>();
registration.setInitParameters(params);
registration.setLoadOnStartup(1);
registration.setUrlMappings(Arrays.asList("/remote_api"));
registration.setName("RemoteApiServlet");
return registration;
}
}

I'd fundamentally misunderstood how the remote-api worked, and didn't realise I needed an app running in app engine to proxy the requests on to Google DataStore. I'm still confused that there is no simple way to access DataStore from Java but this approach is at least working!

Related

Registering a resteasy service in a Quarkus extension

I am currently attempting to write my first Quarkus extension, which needs to deploy a rest service. For that purpose I have been looking at the Quarkus tutorial for creating extensions here, and it is pretty close to what I need to do. The problem is that it shows a Servlet, and I would very much like to deploy a RestEasy(Jackson) service instead.
I have created an extension project and added the following service in the runtime module
package my.test.quarkus.extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/mytest")
public class TestEndpoint {
private static Logger LOGGER = LoggerFactory.getLogger(TestEndpoint.class);
#POST
#Path("/service")
public void testService(){
LOGGER.info("Logit!");
}
}
But the service is not being picked up it seems, as my test returns 404.
package my.test.quarkus.extension.test;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public class ExtensionTest {
#RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withEmptyApplication();
#Test
public void testGreeting() {
RestAssured.when().post("/mytest/service").then().statusCode(200);
}
}
I assume i need a BuildItem registering the service inside the deployment project of my extension, but my question is, which BuildItem do i use? Looking at the list of BuildItems there are loads of them, and even filtering those out that has nothing to do with RestEasy, still leaves over 50, most of them without JavaDoc.
If someone can help me with the problem at hand, simply registering a RestEasy service, as well as an interceptor, from an extension, that would be appreciated. And you will get bonus gratitude if you can explain exactly how to navigate through the Quarkus BuildItems when building extensions, because to me it just seems like a jungle of code, but I am guessing I am missing something.

How to trace incoming requests with AWS X-Ray on Spring Boot WebFlux?

I have multiple microservices running on AWS ECS and I want to try out AWS X-Ray. Following this developer guide I added a WebConfig.java file with a tracing filter.
Added lines to build.gradle:
implementation "javax.servlet:javax.servlet-api:4.0.1"
implementation "com.amazonaws:aws-xray-recorder-sdk-core:2.8.0"
New file WebConfig.java:
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
import javax.servlet.Filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class WebConfig {
#Bean
public Filter tracingFilter() {
return new AWSXRayServletFilter("ordermicroservice");
}
}
However, I don't think this is correct, mostly because I had to add the extra dependency for the javax.servlet.Filter. I think this is because I'm using spring-boot-webflux and not spring-boot-web. So I have a Netty webserver and not a Tomcat webserver.
My questions are:
How can I add logging to a servlet filter, to make sure that every incoming HTTP request is passing correctly through the filter?
What is the correct way to write a web filter in spring-boot-webflux projects, which use Netty and not Tomcat?
Edit:
By now I figured out how to write a filter with Spring Boot WebFlux. I will add it to the question here for future reference:
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
#Component
class MyCustomWebFilter : WebFilter {
override fun filter(webExchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
print("Successfully reached the WebFilter.\n")
val request = webExchange.request
val response = webExchange.response
// TODO: Do the actual filtering by looking at the request and modifying the response
return chain.filter(webExchange)
}
}
As mentioned by Michael, you may need to implement the WebFilter interface. Since the AWSXRayServletFilter is a servlet filter, it won't work with WebFilter. Unfortunately there is no built-in support in X-Ray SDK for WebFlux yet. What you'll need to do is within your WebFilter chain, implement an interceptor of your own for tracing incoming requests by creating a segment and adding relevant data to it. You can reference how the AWSXRayServletFilter traces incoming requests here: https://github.com/aws/aws-xray-sdk-java/blob/master/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/javax/servlet/AWSXRayServletFilter.java
Or, you can use the OpenTelemetry Java SDK to instrument your application and use AWS Collector to send trace data to X-Ray. The OTel SDK has support for WebFlux framework. You can find more info below.
OTel Java SDK instrumentation: https://github.com/open-telemetry/opentelemetry-java-instrumentation
AWS OTel Collector: https://aws-otel.github.io/docs/getting-started/collector

Spring redirect happening to "http://...../login" instead of "https://...../login"

I have deployed a war file generated by spring boot application using oauth2 for single sign on using Azure App service (https only).
When I browse to the home page, the home page loads with a login button.
On clicking the login button a redirect is happening to "http://..../login" (/login is the default sso login path) Since my app service is https only, the http url does not work.
I have tried the redirect_uri settings in the application.property file, but it is not helping.
Has anybody faced this problem?
How can it solved?
I found a similar issue mentioned here
This problem happens when your Tomcat server is behind a proxy. The HTTPS requests terminate at the proxy and the proxy then uses HTTP protocol to communicate to your Tomcat server. You will face this if you deploy your code on cloud providers like Azure (App Service), etc.
For anyone facing this problem, here is the solution:
in application.properties file, add the following. Note: some of the properties have different names in Spring Boot 2.* versions.
security.oauth2.client.pre-established-redirect-uri=https://yourappurl.net/login
security.oauth2.client.registered-redirect-uri=https://yourappurl.net/login
security.oauth2.client.use-current-uri=false
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
server.tomcat.use-relative-redirects=true
server.use-forward-headers=true
server.tomcat.internal-proxies=.*
In your SpringBootApplication class, add the following bean. With Spring Boot <= 2.1.x you had to provide a ForwardedHeaderFilter-Bean. Since Spring Boot 2.2.0 you don't have to do this anymore.
import org.springframework.core.Ordered;
import org.springframework.web.filter.ForwardedHeaderFilter;
#Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
final FilterRegistrationBean<ForwardedHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<ForwardedHeaderFilter>();
filterRegistrationBean.setFilter(new ForwardedHeaderFilter());
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegistrationBean;
}
Add the following line in configure method of your AppConfiguration class:
http.requiresChannel().anyRequest().requiresSecure();
For official info visit this page.
Ranbir has well explained the usecase where someone will face this problem.
I didnt have to add any additional properties to my springboot project. I just had to add ForwardedHeaderFilter as part of spring configuration and mentions the urls where I want this filter to act on x-forwarded header:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import javax.servlet.DispatcherType;
import org.springframework.core.Ordered;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.web.filter.ForwardedHeaderFilter;
import java.security.Security;
import java.util.Arrays;
#SpringBootApplication(scanBasePackages = {"com.abc.xyz"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
log.info("--- Security Overrides ---");
log.info("networkaddress.cache.ttl =" + Security.getProperty("networkaddress.cache.ttl"));
}
#Bean
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(javax.servlet.DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
registration.setUrlPatterns(Arrays.asList("/app/*", "/ping", "/oauth2/authorization/callback", "/oauth/callback"));
return registration;
}
}
Here are some useful references to follow:
https://tomgregory.com/spring-boot-behind-load-balancer-using-x-forwarded-headers/
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#filters-forwarded-headers
https://docs.spring.io/spring-boot/docs/2.0.1.RELEASE/reference/htmlsingle/#howto-enable-https

Sending email using Java and Spring Boot

I am new to sending email using java. I have a Spring Boot project from where I want to send email. I have tried several examples available on internet. Giving the URL of two examples which I tried:
https://www.mkyong.com/spring-boot/spring-boot-how-to-send-email-via-smtp/
https://www.mkyong.com/java/javamail-api-sending-email-via-gmail-smtp-example/
But none is working. Can anyone say me what customizations I will have to do in these examples and which values I will have to change to make them working? Like I will change the to Address to the adress where I will have to send email, what is the username and password? Whose username, password are they?
You can use Sendgrid API. You can send 100 email per day without paying it's absolutely free. Need to create Sendgrid account and generate an API key to send emails.
Reference:
https://sendgrid.com/docs/for-developers/sending-email/v3-java-code-example/
There is Ogham library. The code to send email is easy to write and you can even use a template to write the content of your email. It is easier to use than other libraries because you don't need to handle technical concerns (like inlining images and styles in the HTML, it is done automatically).
You can even test your code locally with a SMTP server to check the result of your email before sending it through a SMTP provider.
The email can be sent either using SMTP protocol or through providers API (like SendGrid).
It is compatible with Spring Boot and even easier to use because you don't need to instantiate the service.
package fr.sii.ogham.sample.springboot.email;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import fr.sii.ogham.core.exception.MessagingException;
import fr.sii.ogham.core.service.MessagingService;
import fr.sii.ogham.email.message.Email;
#SpringBootApplication
#PropertySource("application-email-basic.properties") // just needed to be able to run the sample
public class BasicSample {
public static void main(String[] args) throws MessagingException {
SpringApplication.run(BasicSample.class, args);
}
#RestController
public static class EmailController {
// Messaging service is automatically created using Spring Boot features
// The configuration can be set into application-email-basic.properties
// The configuration files are stored into src/main/resources
#Autowired
MessagingService messagingService;
#PostMapping(value="api/email/basic")
#ResponseStatus(HttpStatus.CREATED)
public void sendMail(#RequestParam("subject") String subject, #RequestParam("content") String content, #RequestParam("to") String to) throws MessagingException {
// send the email using fluent API
messagingService.send(new Email()
.subject(subject)
.body().string(content)
.to(to));
}
}
}
There are many other features and samples / spring samples.

How to get Spring 3.1.1 works with App Engine datastore

Could everyone show me a tutorial on how to make Spring 3.1.1 running on Google App Engine, please?
I've followed severals tutorial and managed to get the dead simple HelloWorld example to work on App Engine. However, when I go futher, I stuck at the persistent process between Spring and Datastore. I looked at this thread Configuring JDO in Spring 3.1?, too, but it works on localhost but it doesn't work when I deploy to app engine due to the javax.naming.NamingException.
Therefore, I'm looking for a not-too-simple tutorial that covers basic aspects of a real life application such as the view, the model and the database.
Jappstart is a good place to see a working example of GAE that uses Spring and the Datastore (via JPA) and is also a good starting point for building a basic GAE/J app.
Having spent about a day trying to make this work, I thought I'd add some additional useful information here. First take a look at this project https://github.com/hleinone/spring-gae-jdo and this issue: http://code.google.com/p/googleappengine/issues/detail?id=1240 -- comment 24 is the useful one.
In case anyone wants to get this working with annotation-driven configuration, here's how I did it:
package com.domain.yourcode.configuration;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jdo.GAETransactionAwarePersistenceManagerFactoryProxy;
import org.springframework.orm.jdo.JdoTransactionManager;
//import org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy;
#Configuration
public class JDOConfiguration {
private static final PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("transactions-optional");
#Bean
public GAETransactionAwarePersistenceManagerFactoryProxy proxyPmf() {
GAETransactionAwarePersistenceManagerFactoryProxy proxy =
new GAETransactionAwarePersistenceManagerFactoryProxy();
proxy.setTargetPersistenceManagerFactory(pmf);
proxy.setAllowCreate(false);
return proxy;
}
#Bean
public JdoTransactionManager transactionManager() {
JdoTransactionManager mgr = new JdoTransactionManager();
mgr.setPersistenceManagerFactory(pmf);
return mgr;
}
}
You'll still want <tx:annotation-driven/> in your applicationContext.xml

Resources