How to get current host and port in micronaut? - java

I'm trying to get current host and port in micronaut application. how do i get it in dynamic manner?
I've tried #Value("{micronaut.server.host}") and #Value("{micronaut.server.port}") but doesn't work.
#Controller("/some/endpoint")
class SomeController {
#Value("{micronaut.server.host}")
protected String host;
#Value("{micronaut.server.port}")
protected Long port;
}

There are a number of ways to do it. One is to retrieve them from the EmbeddedServer.
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.runtime.server.EmbeddedServer;
#Controller("/demo")
public class DemoController {
protected final String host;
protected final int port;
public DemoController(EmbeddedServer embeddedServer) {
host = embeddedServer.getHost();
port = embeddedServer.getPort();
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}

My mistake. As #JaredWare says, we should use Environment to retrieve the property of the application.
#Controller("/some/endpoint")
class SomeController {
#Inject
protected Environment env;
#Get
public String someMethod () {
Optional<String> host = this.env.getProperty("micronaut.server.host", String.class);
return host.orElse("defaultHost");
}
}

The original way you had it is the same as retrieving it from the environment. You were just missing the $ in your #Value annotation.
#Value("${micronaut.server.host}") is equivalent to env.getProperty("micronaut.server.host", String.class)
That will retrieve whatever is configured. If instead you want to retrieve it from the embedded server itself you could do that as well since the actual port may be different from the configured port. That is because it's possible to simply not configure the port or because the configured value is -1 which indicates a random port.

Related

Changing Registered WireMockExtension Port

Is it possible to change WireMockExtension port that has been registered during run time?
#ExtendWith(SpringExtension.class)
public class BaseIntegrationTest {
...
#RegisterExtension
protected WireMockExtension wm = WireMockExtension.newInstance().options(
wireMockConfig().port(12345).extensions(NoKeepAliveTransformer.class)).build();
public void changePort() {
// wm change port to 123
}
}
Any equal solution without using #RegisterExtension but still having the wm variable for stubbing is okay.
WireMockExtension just JUnit extension that manages a WireMock server instance's lifecycle. If you want to change port in runtime you would need to stop and start server.
Looking at WireMockExtension implementation, not sure it was designed for this use case. I would rather recommend to manage WireMock lifecycle without extension.
private WireMockServer wireMockServer;
private int wireMockPort = 1000;
#BeforeEach
void startWireMock() {
wireMockServer = new WireMockServer(wireMockConfig().port(wireMockPort));
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
}
#AfterEach
void stopWireMock() {
wireMockServer.stop();
}
void changePort(int port) {
wireMockPort = port;
}
It would allow you to change port before each test. If you need more granular control and change it during the test you can do nothing like
void changePort(int port) {
stopWireMock();
wireMockPort = port;
startWireMock();
}

Custom rules to validate Springboot configuration

In my Springboot program, I would like to validate my configuration.
Let's say I have a configuration such as:
#Component
#ConfigurationProperties("jms")
public class JmsConfiguration {
protected String brokerType = "";
protected String host = "";
protected String port = "";
}
I know I can use #Validated annotations for very basic checks, e.g.
#Component
#ConfigurationProperties("jms")
#Validated
public class JmsConfiguration {
protected String brokerType = "";
protected String host = "";
#Positive
protected String port = ""; /// must be strictly positive
}
But how can I make more complex checks? For example, how can I ensure that brokerType is whether ems or amq but nothing else?
Or even more complex, how can I ensure that the port is greater than 1000 if, and only if brokerType is amq (stupid case but it is for the example)
I tried to use #Constraint but I did not succeed (however, I am not sure this is the best way to do what I want...)
Thanks to help
Regards,
Philippe
The brokerType can be validated to be either ems or amq by creating a enum for it.
The Spring #ConfigurationProperties has first class support for enum.
Please see example here
https://www.logicbig.com/tutorials/spring-framework/spring-boot/configuration-properties-vs-value-conversion.html
Relevant example From above link
#ConfigurationProperties("app")
public class MyAppProperties {
private int refreshRate;
private TimeUnit refreshTimeUnit;
private Currency tradeCurrency;
.............
}

Springboot autowired null value

I'm fairly new to Spring & Spring boot, trying to wrap my head around with the concepts.
I have this sample class, this is just a typed up code to show what i'm trying to do. There are no compilation errors. When I start the server, the MQConnection class code gets executed, the mq properties from the appplication.properties are read and printing. But when another class tries to call the send message to MQ, i'm seeing NUllPointerException
#Component
public class MQConnection {
private String username;
private String password;
private String host;
private Connection connection;
#Autowired
public MQConnection(#value("${username}") String username,
#value("${password}") String password, #value("${host}") String host) {
this.username = username;
this.password = password;
this.host = host;
init();
}
public getConnection() {
return connection
}
private init() {
connection = mqconnection;
System.out.println(username, password, host); // I see the value when the server is started
//code to connect to mq
}
}
What am I missing, these autowired & beans is really confusing for me as i'm new to Spring world. Am I using right flow or completely absurd, I don't know
#Component
public class MQSendMessage {
#Autowired
MQConnection mqConnection;
public void sendMessage(String message) {
connection = mqConnection.getConnection(); //NULL value
//send messageCode throws nullpointerexception
}
}
public class AnotherClass {
#Autowired
MQSendMessage messageq;
public doSomething() {
messageq.sendMessage("hello world");
}
}
Any help to fix the connection that throws nullpointer
It looks like AnotherClass is not instantiated by Spring container. If you want to use Spring-annotation like convention then you have to annotate your class with e.g.#Component annotation. Otherwise Spring wont instantiate this object for you.
Useful tip
Try using constructor injection instead of a field injection. Just like in your MQConnection class. You can even mark all your class fields instantiated in the construct with final keyword so you will be sure that these values wont change (if they are immutable of course) during bean life cycle. Then AnotherClass could look like this:
public class AnotherClass {
private final MQSendMessage messageq;
#Autowired
public AnotherClass(MQSendMessage messageq) {
this.messageq = messageq
}
public doSomething() {
messageq.sendMessage("hello world");
}
}
Spring Boot documentation
Also please read carefully Spring Boot documentation on Spring Beans and dependency injection. It is very well written and describes basic concepts in details. It will make your learning much easier and faster.
I hope it helps.

AWS Lambda + Spring, how to load application.yml

I have problem with customizing API gateway domain, for my restful app deployed on AWS lambda. Customized domain, works this way, that depending on basePath it chooses different APIs which finally touches Lambda. For example:
api.mycustomdomain.com/view/ping -> goes to application view with path /view/ping
api.mycustomdomain.com/admin/ping -> goes to application admin with path /admin/ping
I am using this example as boilerplate: https://github.com/awslabs/aws-serverless-java-container/tree/master/samples/spring/pet-store
What I would like to achieve is handler which depending on Host header strips prefix from request path.
I have prepared following application.yml file:
server:
contextPath: "/view"
productionHost: "api.mycustomdomain.com"
The problem/question is. How can I now load those into my Lambda function? Here is my naive try:
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
boolean isinitialized = false;
#Value("${server.contextPath}")
private String prefix;
#Value("${server.productionHost}")
private String productionHost;
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
if(awsProxyRequest.getHeaders().get("Host").equals(productionHost))
awsProxyRequest.setPath(awsProxyRequest.getPath().substring(prefix.length()));
if (!isinitialized) {
isinitialized = true;
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PingPongApp.class);
} catch (ContainerInitializationException e) {
e.printStackTrace();
return null;
}
}
return handler.proxy(awsProxyRequest, context);
}
}
Obviously this doesn't work, LambdaHandler is working out of Spring context.
Any ideas how can I deal with that?
It seems you can not load those properties. Follow either of the 2 options given below.
1> You can add following bean in your configuration and that way you can autowire strings and use the way you are already using
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
2>
public AwsProxyResponse..{
#Autowired
private Environment env;
..
public AwsProxyResponse handleRequest{
..
String contextPath = env.getRequiredProperty(“server.contextPath”));
...
}
}

Implement an Undertow reverse proxy that behaves like nginx

For development purposes, not everyone can install nginx on their machines (like our developers on Windows environments), but we want to be able to do a reverse proxy that behaves like nginx.
Here's our very specific case:
we have a spring boot REST service running on http://0.0.0.0:8081
we have spring boot web application running on http://0.0.0.0:8082
We would like to serve both services from http://0.0.0.0:8080
So we would like to map it like this:
requests to http://0.0.0.0:8080/ get proxied to http://0.0.0.0:8082
requests to http://0.0.0.0:8080/api get proxied to http://0.0.0.0:8081
That way it works like nginx with url rewrite reverse proxying.
I checked out the Undertow source code and examples, and even this specific example: Reverse Proxy Example, but this is a load balancer example, I haven't found any example that covers what I need.
Also, I know Undertow is capable of this, because we know we can configure WildFly to cover this specific case without issues through the Undertow component configuration, but we would like to implement it ourselves as a lightweight solution for local development.
Does anyone know of an example to do this? or any documentation that has enough info to implement this? because I've also read Undertow's documentation on reverse proxying and it's not helpful at all.
Thanks
This should do the job.
It's Java8 so some parts may not work on your setup.
You can start it in a similar way as the example you've mentioned in your question.
package com.company
import com.google.common.collect.ImmutableMap;
import io.undertow.client.ClientCallback;
import io.undertow.client.ClientConnection;
import io.undertow.client.UndertowClient;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.ServerConnection;
import io.undertow.server.handlers.proxy.ProxyCallback;
import io.undertow.server.handlers.proxy.ProxyClient;
import io.undertow.server.handlers.proxy.ProxyConnection;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Start the ReverseProxy with an ImmutableMap of matching endpoints and a default
*
* Example:
* mapping: ImmutableMap("api" -> "http://some-domain.com")
* default: "http://default-domain.com"
*
* Request 1: localhost:8080/foo -> http://default-domain.com/foo
* Request 2: localhost:8080/api/bar -> http://some-domain.com/bar
*/
public class ReverseProxyClient implements ProxyClient {
private static final ProxyTarget TARGET = new ProxyTarget() {};
private final UndertowClient client;
private final ImmutableMap<String, URI> mapping;
private final URI defaultTarget;
public ReverseProxyClient(ImmutableMap<String, URI> mapping, URI defaultTarget) {
this.client = UndertowClient.getInstance();
this.mapping = mapping;
this.defaultTarget = defaultTarget;
}
#Override
public ProxyTarget findTarget(HttpServerExchange exchange) {
return TARGET;
}
#Override
public void getConnection(ProxyTarget target, HttpServerExchange exchange, ProxyCallback<ProxyConnection> callback, long timeout, TimeUnit timeUnit) {
URI targetUri = defaultTarget;
Matcher matcher = Pattern.compile("^/(\\w+)(/.*)").matcher(exchange.getRequestURI());
if (matcher.find()) {
String firstUriSegment = matcher.group(1);
String remaininguri = matcher.group(2);
if (mapping.containsKey(firstUriSegment)) {
// If the first uri segment is in the mapping, update the targetUri
targetUri = mapping.get(firstUriSegment);
// Strip the request uri from the part that is used to map upon.
exchange.setRequestURI(remaininguri);
}
}
client.connect(
new ConnectNotifier(callback, exchange),
targetUri,
exchange.getIoThread(),
exchange.getConnection().getByteBufferPool(),
OptionMap.EMPTY);
}
private final class ConnectNotifier implements ClientCallback<ClientConnection> {
private final ProxyCallback<ProxyConnection> callback;
private final HttpServerExchange exchange;
private ConnectNotifier(ProxyCallback<ProxyConnection> callback, HttpServerExchange exchange) {
this.callback = callback;
this.exchange = exchange;
}
#Override
public void completed(final ClientConnection connection) {
final ServerConnection serverConnection = exchange.getConnection();
serverConnection.addCloseListener(serverConnection1 -> IoUtils.safeClose(connection));
callback.completed(exchange, new ProxyConnection(connection, "/"));
}
#Override
public void failed(IOException e) {
callback.failed(exchange);
}
}
}
As per M. Deinum's comment suggestion, I'll use Zuul Spring Boot component instead of trying to do this with Undertow, as it's more fit for this task.
Here's a link on a tutorial to do this:
https://spring.io/guides/gs/routing-and-filtering/
Hope this helps anyone else, as this is a pretty common case, and I didn't know about Zuul on Spring Boot.

Categories