Can I retrieve the discovery client instance id/name ? - spring-cloud

I have deployed an eureka server on cloud foundry together with a micro service "helloworld". Then I scaled the "helloworld" to two instances, thus I find that in the eureka dashboard there are two instances of "helloworld" registered.
I am wondering in runtime when I consuming the "helloworld" is there a way to know which instance is being called? Cloud foundry assigns two random IDs for them. Basically if I can retrieve the random ID that will be good.

Use following in your springbootapplication class(main class) to print all the instances in terminal/console:
#Component
class DiscoveryClientSample implements CommandLineRunner {
#Autowired
private DiscoveryClient discoveryClient;
#Override
public void run(String... strings) throws Exception {
System.out.println(discoveryClient.description());
discoveryClient.getInstances("helloworld").forEach((ServiceInstance serviceInstance) -> {
System.out.println("Instance --> " + serviceInstance.getServiceId()
+ "\nServer: " + serviceInstance.getHost() + ":" + serviceInstance.getPort()
+ "\nURI: " + serviceInstance.getUri() + "\n\n\n");
});

You can use Eureka's EurekaClient class to get information about a particular service. http://cloud.spring.io/spring-cloud-static/Camden.SR3/#_using_the_eurekaclient

Related

Resource not exported up in OSGi container

I'm trying to expose a REST service through OSGi (using Apache Felix). I'm using the osgi-jax-rs-connector to publish the resource. Here is the resource interface:
#Path("/bingo")
public interface BingoService {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/lottery")
List<Integer> getLottery();
}
The implementation uses DS annotation to obtain reference to a provided service in container:
#Component(
immediate = true,
service = BingoService.class,
properties = "jersey.properties")
public class Bingo implements BingoService {
#Reference
private RandomNumberGenerator rng;
#Activate
public void activateBingo() {
System.out.println("Bingo Service activated using " +
rng.getClass().getSimpleName());
}
#Override
public List<Integer> getLottery() {
List<Integer> result = new ArrayList<>();
for (int i = 5; i > 0; i--) {
result.add(rng.nextInt());
}
return result;
}
}
jersey.properties simply contains this line
service.exported.interfaces=*
When I deploy the bundle it starts and register the service correctly. But if I go to http://localhost:8181/services/bingo/lottery I get 404.
Could someone point me to the issue or give me some advice on where to look?
On reading the documentation for OSGi - JAX-RS Connector, it expects to find the annotations #Path or #Provider on the service instance object. You have placed them instead on an interface implemented by the component.
I'm not sure what the purpose of the BingoService interface is. This is not required for JAX-RS services. Normally you would register the resource class using its own type (e.g. service=Bingo.class) or simply java.lang.Object.

How to use regexmapper based routing in Zuul? PatternServiceRouteMapper not working?

What I try to achieve is a routing for example:
http://zuul-host:8080/v1/foo/hello to my service foo-v1, resource hello
I'm trying out the regexmapper example described at http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html
My problem is that I see that a service called foo-v1 gets mapped to /v1/foo in the PatternServiceRouteMapper but then I'm not able to call that route. It's also no visible at /mappings. Do I have to activate that route somewhere?
Setup
Foo Service
application.properties
server.port=9092
spring.application.name=foo-v1
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.healthcheck.enable=true
Zuul
My configuration class Routings.java. I added some sysout log output for the service mapping and I get foo-v1 -> v1/foo in the log. Therefore this mapping should be active.
#Configuration
public class Routings {
#Bean
public PatternServiceRouteMapper serviceRouteMapper() {
return new PatternServiceRouteMapper(
"(?<name>^.+)-(?<version>v.+$)",
"${version}/${name}") {
#Override
public String apply(final String serviceId) {
String route = super.apply(serviceId);
System.out.println(serviceId + " -> " +route);
return route;
}
};
}
}
My ZuulApplication.java
#SpringBootApplication
#EnableZuulProxy
#ComponentScan
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
#RefreshScope
#ConfigurationProperties("zuul")
public ZuulProperties zuulProperties() {
return new ZuulProperties();
}
}
Ok, found the solution.
Remove ignoredServices: '*' from the zuul config.
This happens if you work through the examples. They start with explicitly configured routes and ignore dynamic routings. It's in the documentation but made no sense to me at that point :-)
To skip having a service automatically added, set zuul.ignored-services to a list of service id patterns.
When using the regexmapper we start using services that get added automatically and that's the feature we disabled with ignoredServices: '*'

Test the remote client jndi lookup using arquillian

Setup: arquillian, jboss as 7.1.1.final as a managed Container
I am currently migrating an EJB application from EJB 2.x to 3.x and JBoss 3.x to JBoss AS 7.1.
During this process i would like to get most classes under test and stumbled over arquillian.
While arquillian seems to offer some nice features on inter-bean-functionality i cannot figure out whether or not the testing of remote client features using jndi lookups works or not.
I used the Arquillian Getting started guides on my beans which worked, but since these are using #Inject and in my application jndi lookups are used everywhere i (at least think that i) need to swerve from that path.
Here is the TestCase i created based on Arquillian Getting Started. I explicitly left in all attempts using jndi properties of which i thought they might help.
The Test
should_create_greeting()
works if the Greeter bean using a separate Producer.
#RunWith(Arquillian.class)
public class GreeterTest {
public static final String ARCHIVE_NAME = "test";
Logger logger = Logger.getLogger(GreeterTest.class.getName());
#Deployment
public static Archive<?> createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class, ARCHIVE_NAME + ".jar").addPackage(Greeter.class.getPackage())
.addAsManifestResource("test-persistence.xml", "persistence.xml").addAsManifestResource("OracleGUIDS-ds.xml")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return jar;
}
/**
* #Inject works using a producer with {#code #Produces}
*/
// #Inject
// Greeter greeter;
#ArquillianResource
Context context;
GreeterRemote greeter;
#Before
public void before() throws Exception {
Map<String, String> env = new HashMap<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.as.naming.InitialContextFactory");
env.put("jboss.naming.client.ejb.context", "true");
// env.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT",
// "false");
// env.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS",
// "false");
// env.put("jboss.naming.client.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED",
// "false");
for (Map.Entry<String, String> entry : env.entrySet()) {
context.addToEnvironment(entry.getKey(), entry.getValue());
}
greeter = (GreeterRemote) context.lookup(ARCHIVE_NAME + "/" + Greeter.class.getSimpleName() + "!"
+ GreeterRemote.class.getName());
}
#Test
public void should_create_greeting() {
Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling"));
greeter.greet(System.out, "Earthling");
}
}
Is it possible to get this test running with jndi lookup? Am i missing something?
If you want to test the Remote features of a EJB you probably want to run on the client side and not in container.
You can configure the Deployment to be only client side by using #Deployment(testable=false). The #Test methods will then run as if you were a remote client.
Beyond that you can just lookup the bean via the injected Context if you want.
I had the same issue, so in a workaround i just added on the method to be tested the remoteejb as a parameter.
On my ejb:
public List localBean.obtain(RemoteEJB remoteEjb){
return remoteEjb.obtain();
}
Then on the arquillian test :
#Inject
private LocalBean localBean;
#Inject
private RemoteEJB remoteEjb;
#Test
public void test(){
List<Vo>voList = localBean.obtain(remoteEjb);
}
The best part is the remote ejb its injected and on the caller method original
#EJB(lookup="java:global/ear/ejb/RemoteEjb")
private RemoteEJB remoteEjb;

Spring AOP and AspectJ. Need advice/comments on Around Advice

I posted this on another forum and wanted to see if I can reach more people.
I am working on an application that consists of different Spring web apps.
Say we have:
ComponentA.jar
ComponentB.jar
And WAR files:
Foo.war (contains ComponentA)
Baa.war (contains ComponentA &
ComponentB)
We are using Logback to log to our debug log. So say that the various classes of the application have the the following logger declaration:
private static final Log log = LoggerFactory.getLogger(NAME_OF_WAR_FILE + "." + NAME_OF_CONTAINING_COMPONENT + "." + PACKAGE.CLASS_NAME);
So example:
package a.b.c;
public class SomeClass {
private static final Log log = LoggerFactory.getLogger("Foo.war" + "." + "ComponentA" + "." + SomeClass.class);
}
package x.y.z;
public class SomeOtherClass {
private static final Log log = LoggerFactory.getLogger("Baa.war" + "." + "ComponentA" + "." + SomeOtherClass .class);
}
Assume that the name of the war file and component is set by a property and not hard-coded.
Is it possible to have an Aspect and Advice that can do something like the following (pseudo since I'm not sure it can be done):
#Aspect
public class TheAspect{
#Around("execution of a method")
public Object aroundSomething(ProceedingJoinPoint pjp){
Log log = get the log instance from the class that this advice is running on
if(log.isDebugEnabled())
// log something
Object o = pjp.proceed();
if(log.isDebugEnabled())
// log something else
return o;
}
}
The point here is to write to the log file using the log of the class instance which contains the method which is being intercepted by the Advice.
The application is presented as a single web app composed of Foo.war and Baa.war. Both Foo.war and Baa.war write to the same log file.
Example:
2011-09-22 14:35:35.159 MDT,DEBUG,Foo.war.ComponentA.a.b.c.SomeClass,Hello World Debug message
2011-09-22 14:35:35.159 MDT,DEBUG,Baa.war.ComponentA.a.b.c.SomeClass,Hello World Debug message
2011-09-22 14:35:35.159 MDT,DEBUG,Baa.war.ComponentB.x.y.z.SomeOtherClass,Hello World Debug message
Thanks in advance.
You can use thisJoinPoint inside your aroundSomething method.
To get the class name:
Signature sig = thisJoinPoint.getSignature();
String className = sig.getDeclaringTypeName();
You can also get the class object:
Class<?> type = sig.getDeclaringType();
And maybe you can use the package to identify your war file:
Package pack type.getPackage();

Application Client EJB Eclipse Glassfish

I'm using GlassFish Tools Bundle for Eclipse.
I need to create a bean and a client that tests it. The bean (and its interface) are the following.
package mykPK;
import java.math.BigDecimal;
import javax.ejb.*;
#Stateless
public class ConverterBean implements Converter {
private BigDecimal yenRate = new BigDecimal("115.3100");
private BigDecimal euroRate = new BigDecimal("0.0071");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2, BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2, BigDecimal.ROUND_UP);
}
}
The interface:
package mykPK;
import java.math.BigDecimal;
import javax.ejb.Remote;
#Remote
public interface Converter {
public BigDecimal dollarToYen(BigDecimal dollars);
public BigDecimal yenToEuro(BigDecimal yen);
}
I create them correctly in an EJB project and run them "as a server". All seems to start correctly.
Now I want to create a client.
I tried to put the client inside the same project, creating a different project ("Application Client Project") or even creating a more general "E application project" with two subproject. The result is the same.
Now, the client code is the following
import java.math.BigDecimal;
import javax.ejb.EJB;
import mykPK.Converter; /*of course to to that, i reference in the client project the
EJB project*/
public class ConverterClient {
#EJB private static Converter converter;
public ConverterClient(String[] args) {
}
public static void main(String[] args) {
ConverterClient client = new ConverterClient(args);
client.doConversion();
}
public void doConversion() {
try {
BigDecimal param = new BigDecimal("100.00");
BigDecimal yenAmount = converter.dollarToYen(param);
System.out.println("$" + param + " is " + yenAmount
+ " Yen.");
BigDecimal euroAmount = converter.yenToEuro(yenAmount);
System.out.println(yenAmount + " Yen is " + euroAmount
+ " Euro.");
System.exit(0);
} catch (Exception ex) {
System.err.println("Caught an unexpected exception!");
ex.printStackTrace();
}
}
}
When I run this file, i always get the same:
Caught an unexpected exception!
java.lang.NullPointerException
at ConverterClient.doConversion(ConverterClient.java:17)
at ConverterClient.main(ConverterClient.java:12)
I suppose this is beacause my client is not in the same container of the bean, and it is not "deployed" (I simply run the file). But when I tried the more general "Enterprise Application Project" the results were the same)
So, where to put the client and give him the access (with #EJB) to the Bean??
You're trying to inject into a non-managed object. You need to grab the initial context and look it up.
Pretty much the same thing as here:
cannot find my bean using the InitialContext.lookup() method
The stack trace suggests that you've directly launched the main method. In order to use injection in the main class, you must use the application client container.
A good example of this working can be found here Packaging your client for use with glassfish's application client container (via the "appclient" command) is shown, as is packaging it as a standalone Java app.