I have to implement my project as microservice arch. For that I am doing one sample app using Spring Boot of adding two no. I have three services. Here is my registration-server.yml.Similarly I have account-server.yml and user-service.yml. I want to call add() using UserService.java without RMI concept, since I am using Spring Boot. Also I don't want REST call since it will be costly for my project. How can I manually write code for lookup() in UserService so that it can call Adder?
#EnableAutoConfiguration
#EnableDiscoveryClient
public class AddService {
public static int add(int x,int y){
int z=x+y;
System.out.println("The sum of no. is "+z);
return z;
}
public static void main(String[] args) {
System.setProperty("spring.config.name", "add-service");
SpringApplication.run(AddService.class, args);
}
#SpringBootApplication
#EnableEurekaServer
public class RegistrationService {
public static void main(String[] args) {
// Tell server to look for registration.properties or registration.yml
System.setProperty("spring.config.name", "registration-service");
SpringApplication.run(RegistrationService.class, args);
}
#SpringBootApplication
#EnableDiscoveryClient
public class UserService {
public static void main(String[] args) {
System.setProperty("spring.config.name", "registration-service");
SpringApplication.run(UserService.class, args);
}
eureka:
instance:
hostname: localhost
client: # Not a client, don't register with yourself
registerWithEureka: false
fetchRegistry: false
server:
port: 1111 # HTTP (Tomcat) port
I'd say that the Spring Cloud guide on this is the best starting point.
But in short, since you're using Spring Cloud (i.e. #EnableDiscoveryClient), I'd personally use Spring Cloud's feign client support to carry out the call. This will do the actual discovery service (eureka) lookup and HTTP calls for you.
Firstly you'll need the #EnableFeignClients annotation on your config class, and the following dependency (assuming Maven):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
Then within your user service project, you can add the following interface:
#FeignClient("add-service")
public interface AddServiceClient {
#RequestMapping(method = RequestMethod.POST, value = "/add/{x}/{y}", consumes="application/json")
int addNumbers(#PathVariable("x") int x, #PathVariable("y") int y);
}
That's basically it really. You can then autowire AddServiceClient and use it:
#Autowired
private AddServiceClient addServiceClient;
void someMethod() {
addServiceClient.addNumbers(2, 4);
}
This assumes that you expose /add/{x}/{y} as a POST endpoint within your add service (e.g. via #RestController and #RequestMapping)
EDIT: Sorry, I just seen where you said REST would be costly. Why do you think that? :)
Related
I am currently using Spring Cloud Function, and I want to deploy my functions on AWS Lambda, using the adapter for AWS.
Till now all the Spring Cloud Function implemented was a single function with the following structure.
#SpringBootApplication
#ComponentScan(basePackages = "com.demo")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Handler for lamda:
import org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler;
public class DemoHandler extends SpringBootRequestHandler<DemoRequest, DemoResponse> {
}
I understand that this part of the code is Depricated.
Interface to define the Endpoint:
#SuppressWarnings("hiding")
#FunctionalInterface
public interface Demoapi<DemoRequest, DemoResponse>{
#PostMapping(value = "/v1/demo")
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo);
}
Followed by the controller where the #RestController was defined
Part of the controller:
#RestController
public class Democontroller implements Demoapi<DemoRequest, DemoResponse>{
static final Logger LOGGER = LogManager.getLogger(Democontroller.class);
#Autowired
private DemoService demoService;
#Override
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo) {
DemoResponse demoResponse=new DemoResponse();
try {
demoResponse = demoService.demofun(demoInfo);
........
.........
.........
This setup works perfectly. When deploying to Lambda, I provide Handler as com.demo.DemoHandler in the AWS console, and in the Environment Variable under FUNCTION_NAME I give the DemoController class with the Starting letter in small caps i.e demoController and the lamda works fine when tested in the console as well as directed from an API Gateway.
Currently I am working on a project where it is required to perform all the CRUD operations within one Spring Boot Application and deploy it as a single lambda.
I want to know how this can be achieved using similar structure within the application.
All help is highly appreciated!
You can achieve a similar result using an approach with this library:
https://github.com/MelonProjectCom/lambda-http-router
#PathPostMapping("/example/test")
public String example(#Body String body) {
return "Hello World! - body: " + body;
}
I have implemented a Spring Cloud based application that has the following principal architecture. The GatewayService delegates to multiple instances of HelloService. I use Consul for service discovery. GatewayService is implemented in the following way:
#EnableDiscoveryClient
#SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
#Bean
#LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
#RestController
static class GateWayController {
private final WebClient.Builder webClientBuilder;
#Data
#NoArgsConstructor
private static class Response {
private String message;
private String appName;
private int port;
}
#Autowired
public GateWayController(WebClient.Builder webClientBuilder) {
this.webClientBuilder = webClientBuilder;
}
#GetMapping("/lbhello")
public Mono<Response> hello() {
return webClientBuilder.build()
.get()
.uri("lb://hello-service/hello")
.retrieve()
.bodyToMono(Response.class);
}
}
}
spring-cloud-starter-consul-discovery and spring-cloud-starter-loadbalancer areincluded as a dependencies in pom.xml. In application.yaml I only set application.name and disable Ribbon, so that Spring Cloud Loadbalancer is used. When I start the GatewayService and two instances of HelloService everything works as expected. Service calls to the lbhello endpoint are delegated alternately to the two HelloService instances.
When one instance of HelloService is stopped, Consul detects that the instance is missing an marks it as critical. As a consequence I would expect that the load balancer stops forwarding requests to the non-existing service, at least after some time. But this never happens. Every second service call is delegated to this service instance and consequently fails. When using Eureka as a discovery service the registry is adapted and only the remaining HelloService is considered for service calls.
Is this the expected behavior for Consul? If the answer is yes, is there a Spring Cloud setting to adapt this behavior?
The Spring Cloud ConsulDiscoveryClient loads all service instance by default, regardless of their health state. This behavior can be changed by the following configuration setting:
spring.cloud.consul.discovery.query-passing = true
With this setting request are not delegated to non-healty instances after some delay.
I'm developing a Spring Integration/Boot application. I'm using a multi-document application.yml (src/main/resources/application.yml) to set defaults for several configuration classes (annotated with #ConfigurationProperties). Applicaiton.yml comes with defaults, and some of them need to be overridden, depending on the environment.
I'm open to either using Java system properties (-D...=...), Spring "properties" (--...=...), or preferably a yaml file located outside a Jar, in a directory.
Application.yml has 4 documents and each one corresponds to a different configuration class. Let's just focus on ServerConfig:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(locations = "classpath:application.yml", prefix = "server")
public class ServerConfig {
private Integer port;
private String address;
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Application.yml:
server:
address: ::1
port: 8080
---
Notice how I have locations specified in the annotation. This loads application.yml and uses those values successfully, but I can't figure out how to override them (say -Dserver.port=7777 or --server.port=7777). If I remove locations = ..., then I can use `-Dserver.port=7777, but the defaults in application.yml are never loaded, so I have to specify every single value as a command line argument.
I've read through https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html numerous times, and I can't understand why I can't leave locations = 'application.yml' in the config annotations and selectively override with system properties.
Does anyone know how to do this?
Sigh. It was a problem with application startup -- caused by confusion on my part between Spring Integration and Spring Boot.
My Main method used to be:
#SpringBootApplication
public class Main {
public static void main(String... args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("org.fandingo.blah.blah");
ctx.registerShutdownHook();
}
My understanding is that's how you'd start an application that is Spring Integration only (assuming JavaConfig of course). The problem is that the YAML properties loading is a Spring Boot feature. Switching out the main method to use Spring Boot method fixed the issue:
#SpringBootApplication
public class Main {
public static void main(String... args) {
SpringApplication app = new SpringApplication(Main.class);
app.setRegisterShutdownHook(true);
app.run(args);
}
}
Spring is so goddamned complex and cryptic. Why Spring Integration and Boot can't naturally cooperate is beyond me.
I am using spring boot. I am developing a web application and i need to get the app base to generate a link for an email. For that i need to get the base URL. As the tomcat is embedded
request.getContextpath()
returns null. I need to get that localhost:8080 dynamically so that when I deploy this to server I don't have to change the code.
In a spring boot application you can change the server context in the application.properties with the properties server.context-path=your-path
in your code you can refer to this properties like below
#SpringBootApplication
#EnableConfigurationProperties(ServerProperties.class)
public class DemoApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Autowired
ServerProperties serverProperties;
#Override
public void run(String... strings) throws Exception {
System.out.println("serverProperties.getContextPath(): " + serverProperties.getContextPath());
}
}
The key point here is use #EnableConfigurationProperties(ServerProperties.class) on your #Configuration class and then use the injected fiels ServerProperties for consume the value.
I hope that this can help you...
Huh, that took me some time to get there but:
#SpringBootApplication
public class TestServiceApplication {
public static void main(String[] args) {
AnnotationConfigEmbeddedWebApplicationContext context = (AnnotationConfigEmbeddedWebApplicationContext)SpringApplication.run(TestServiceApplication.class, args);
TomcatEmbeddedServletContainer tomcatContainer = (TomcatEmbeddedServletContainer)context.getEmbeddedServletContainer();
String host = tomcatContainer.getTomcat().getHost().getName();
int port = tomcatContainer.getPort();
System.out.println("Host:port " + host + ":" + port);
}
}
Output: Host:port localhost:8080
Is that what You wanted?
I am looking for the way to add embedded elasticsearch to my spring boot integration test.
I looked at elastic search integration test but it does not work together with spring boot as both should uses different test runner.
I have a class test as below unfortunately it does not work with error:
java.lang.IllegalStateException: No context information for thread:
Thread[id=1, name=main, state=RUNNABLE, group=main]. Is this thread
running under a class
com.carrotsearch.randomizedtesting.RandomizedRunner runner context?
Add #RunWith(class
com.carrotsearch.randomizedtesting.RandomizedRunner.class) to your
test class. Make sure your code accesses random contexts within
#BeforeClass and #AfterClass boundary (for example, static test class
initializers are not permitted to access random contexts).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#IntegrationTest("server.port:0")
public class TestExample extends ElasticsearchIntegrationTest {
TestRestTemplate testRestTemplate = new TestRestTemplate();
#Value("${local.server.port}")
int port;
#Test
public void testOne(){
ResponseEntity<String> results = testRestTemplate.getForEntity(String.format("http://localhost:%d/client/1", port), String.class);
System.out.print(results);
}
}
Does anybody has some ideas how to make them run or what is alternatives ??
You can actually do what you need without any additional elasticsearch testing dependencies. The idea is basically to create an embedded node and then use the NodeClient to communicate with it.
For that, I created my own EmbeddedElasticsearchServer class which looks (more or less) like this:
public class EmbeddedElasticsearchServer implements InitializingBean {
public EmbeddedElasticsearchServer() {
ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
.put("http.enabled", "false")
.put("path.data", "target/elasticsearch-data");
node = nodeBuilder()
.local(true)
.settings(elasticsearchSettings.build())
.node();
client = node.client();
}
#Override
public void afterPropertiesSet() throws Exception {
// Initialization stuff:
// - create required indices
// - define mappings
// - populate with test data
}
public Client getClient() {
return client;
}
}
Then, in spring configuration (let's call it integration-test-context.xml) I did this:
<bean id="embeddedElasticsearchServer"
class="com.example.EmbeddedElasticsearchServer" />
<bean id="elasticsearchClient"
class="org.elasticsearch.client.node.NodeClient"
factory-bean="embeddedElasticsearchServer"
factory-method="getClient" />
Then you can just autowire the client in your test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/integration-test-context.xml")
public abstract class AbstractElasticsearchIntegrationTest {
#Autowired
private Client elasticsearchClient;
// Your rests go here...
}