ShrinkWrap Maven resolver can't find war artifact in local repo - maven

I've an integration test where I'm deploying 2 web services in Wildfly using Arquillian and ShrinkWrap resolver. Both services are independent such that neither is dependent on the other in any Maven dependency sense. Service 2 makes a HTTP call to service 1. This is purely a B2B scenario where one component calls out to another.
That said, here's my test. Problem is, Arquillian fails to deploy the service 1. Since ShrinkWrap error messages are just useless (there're actually no messages), I'm trying to figure out what am I doing wrong. I've verified that the artifact for service 1 does exist in my local Maven repo.
#Deployment(name = AVAILABILITY_SERVICE_NAME, order = 1)
public static WebArchive createAvailabilityServiceDeployment() {
WebArchive availabilityService = Maven.configureResolver()
.workOffline().withMavenCentralRepo(false)
.withClassPathResolution(true)
.resolve(AVAILABILITY_SERVICE_MVN_COORD).withoutTransitivity()
.asSingle(WebArchive.class);
System.out.println(availabilityService.toString(true));
return availabilityService;
}
#Deployment(name = APPOINTMENT_SERVICE_NAME, order = 2)
public static WebArchive createAppointmentServiceDeployment()
throws FileNotFoundException {
WebArchive appointmentService = create(WebArchive.class,
APPOINTMENT_SERVICE_NAME + ".war").addPackages(true,
Filters.exclude(".*Test.*"), AppointmentApp.class.getPackage())
.addAsWebInfResource(EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
System.out.println(appointmentService.toString(true));
return appointmentService;
}
java.lang.RuntimeException: Could not invoke deployment method: public static org.jboss.shrinkwrap.api.spec.WebArchive name.abhijitsarkar.microservices.appointment.AppointmentResourceIT.createAvailabilityServiceDeployment()
at org.jboss.shrinkwrap.resolver.spi.format.FormatProcessors.find(FormatProcessors.java:53)
at org.jboss.shrinkwrap.resolver.impl.maven.MavenFormatStageImpl.as(MavenFormatStageImpl.java:82)
at org.jboss.shrinkwrap.resolver.impl.maven.MavenFormatStageImpl.asSingle(MavenFormatStageImpl.java:100)
at name.abhijitsarkar.microservices.appointment.AppointmentResourceIT.createAvailabilityServiceDeployment(AppointmentResourceIT.java:50)

Apparently, it needs to be done in 2 steps. Hope it helps someone else.
private static WebArchive createDependentServiceDeployment(String name) {
String mvnCoordinate = join(":", DEPENDENT_SERVICE_GROUP, name,
DEPENDENT_SERVICE_PACKAGING, DEPENDENT_SERVICE_VERSION);
File service = Maven.configureResolver().workOffline()
.withMavenCentralRepo(false).withClassPathResolution(true)
.resolve(mvnCoordinate).withoutTransitivity().asSingleFile();
return ShrinkWrap.create(ZipImporter.class,
join(".", name, DEPENDENT_SERVICE_PACKAGING))
.importFrom(service).as(WebArchive.class);
}

Related

Spring Boot microservices - dependency

There are two microservices deployed with docker compose. A dependecy between services is defined in docker compose file by depends_on property. Is it possible to achieve the same effect implicitly, inside the spring boot application?
Let's say the microservice 1 depends on microservice 2. Which means, microsearvice 1 doesn't boot up before microservice 2 is healthy or registered on Eureka server.
By doing some research, I found a solution to the problem.
Spring Retry resolves dependency on Spring Cloud Config Server. Maven dependency spring-retry should be added into the pom.xml, and the properties below into the .properties file:
spring.cloud.config.fail-fast=true
spring.cloud.config.retry.max-interval=2000
spring.cloud.config.retry.max-attempts=10
The following configuration class is used to resolve dependency on other microservices.
#Configuration
#ConfigurationProperties(prefix = "depends-on")
#Data
#Log
public class DependsOnConfig {
private List<String> services;
private Integer periodMs = 2000;
private Integer maxAttempts = 20;
#Autowired
private EurekaClient eurekaClient;
#Bean
public void dependentServicesRegisteredToEureka() throws Exception {
if (services == null || services.isEmpty()) {
log.info("No dependent services defined.");
return;
}
log.info("Checking if dependent services are registered to eureka.");
int attempts = 0;
while (!services.isEmpty()) {
services.removeIf(this::checkIfServiceIsRegistered);
TimeUnit.MILLISECONDS.sleep(periodMs);
if (maxAttempts.intValue() == ++attempts)
throw new Exception("Max attempts exceeded.");
}
}
private boolean checkIfServiceIsRegistered(String service) {
try {
eurekaClient.getNextServerFromEureka(service, false);
log.info(service + " - registered.");
return true;
} catch (Exception e) {
log.info(service + " - not registered yet.");
return false;
}
}
}
A list of services that the current microservice depends on are defined in .properties file:
depends-on.services[0]=service-id-1
depends-on.services[1]=service-id-2
A bean dependentServicesRegisteredToEureka is not being initialized until all services from the list register to Eureka. If needed, annotation #DependsOn("dependentServicesRegisteredToEureka") can be added to beans or components to prevent attempting an initialization before dependentServicesRegisteredToEureka initialize.

Share application.properties files in different project

Below showing the project structure
Core Project
|-config project
|
|-Service project
After building the core project we get Service.jar file.
While running the service.jar am passing spring.config.additional.location as command line argument.
java -jar Service-1.0.jar --spring.config.additional-location=C:/Users/Administrator/Desktop/Springboot/
above spring.config.additional.location path having application.property file and some xml files.
I can able to read application property file in service project ,following logic
Application.propertes
external.config=C:/Users/Administrator/Desktop/Springboot/config/
Mian Class
#ImportResource(locations = {
"${external.config}"+"/spring/service-config.xml",
"${external.config}"+"/spring/datasource-config.xml"
})
public class ServiceMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(ServiceMain.class)
.build()
.run(args);
for (String name : applicationContext.getBeanDefinitionNames()) {
}
}
}
Similar kind of logic applied in config project is given below,its not working
#Configuration
public class ConfigurationFactory
{
#Value("${external.config}")
public String extConfPath;
public String REQ_CONF = extConfPath+"/Configuration.xml";
public static final String FILTER_XML_CONF = extConfPath+"/DocFilter.xml";
}
Is there any better way to do this? How can i read external application.properties in config project
Do we have any better way to do this in spring boot
As you are cleary developing a distributed web system the best practice is to used externalised configuration used by your different services allowing you to update settings without redeployment. Take a look at Spring Cloud Config

How to use SpringBoot actuator over JMX

I am having existing Spring Boot application and I want to do monitoring the application through actuator.I tried with http endpoints and it is working fine for me. Instead of http end points I need JMX end points for my existing running application.
If you add spring-boot-starter-actuatordependency in your build.gradle or pom.xml file you will have JMX bean enabled by default as well as HTTP Endpoints.
You can use JConsole in order to view your JMX exposed beans. You'll find more info about this here.
More details about how to access JMX endpoints here.
Assuming you're using a Docker image where the entry point is the Spring Boot app using java in which case the PID is "1" and so would the Attach API's Virtual Machine ID. You can implement a health probe as follows.
import com.sun.tools.attach.spi.AttachProvider;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class HealthProbe {
public static void main(String[] args) throws Exception {
final var attachProvider = AttachProvider.providers().get(0);
final var virtualMachine = attachProvider.attachVirtualMachine("1");
final var jmxServiceUrl = virtualMachine.startLocalManagementAgent();
try (final var jmxConnection = JMXConnectorFactory.connect(new JMXServiceURL(jmxServiceUrl))) {
final MBeanServerConnection serverConnection = jmxConnection.getMBeanServerConnection();
#SuppressWarnings("unchecked")
final var healthResult =
(Map<String, ?>)
serverConnection.invoke(
new ObjectName("org.springframework.boot:type=Endpoint,name=Health"),
"health",
new Object[0],
new String[0]);
if ("UP".equals(healthResult.get("status"))) {
System.exit(0);
} else {
System.exit(1);
}
}
}
}
This will use the Attach API and make the original process start a local management agent.
The org.springframework.boot:type=Endpoint,name=Health object instance would have it's health method invoked which will provide a Map version of the /actuator/health output. From there the value of status should be UP if things are ok.
Then exit with 0 if ok, or 1 otherwise.
This can be embedded in an existing Spring Boot app so long as loader.main is set. The following is the HEALTHCHECK probe I used
HEALTHCHECK --interval=5s --start-period=60s \
CMD ["java", \
"-Dloader.main=net.trajano.swarm.gateway.healthcheck.HealthProbe", \
"org.springframework.boot.loader.PropertiesLauncher" ]
This is the technique I used in distroless Docker Image.
Side note: Don't try to put this in a CommandLineRunner interface because it will try to pull the configuration from the main app and you likely won't need the whole web stack.

Intellij cannot resolve methods of classes from external library

I am working with the IBM Watson service and imported the library through a maven dependency.
I assumed everything went fine, as all the classes are shown in the external library section:
Instantiating classes works fine, but if I try to use methods from those classes Intellij says "cannot resolve symbol 'methodname'".
public class Watson
{
ConversationService service = new ConversationService("2017-07-02");
service.setUsernameAndPassword("username", "password");
NaturalLanguageClassifier n = new NaturalLanguageClassifier();
n.createClassifier()
}
I have already tried the invalidate caches action and tried other tricks I could find on the internet, but nothing worked... What do I do wrong? Is there any option I have to tick so that Intellij finds the methods?
You have to put the 4 lines within a method. For simplification, I put it in main.
public class Watson
{
public static void main(String[] args)
{
ConversationService service = new ConversationService("2017-07-02");
service.setUsernameAndPassword("sss", "ttt");
NaturalLanguageClassifier n = new NaturalLanguageClassifier();
n.createClassifier("name", "en", new File("/tmp/data"));
}
}

Intermittent issue with Remote EJB invocation on WAS 7.0

We have a business functionality which is exposed as a Remote EJB. We shared the remote interface with the client. Below is the Remote interface that we shared.
public interface EmployeeRemoteEJB {
public Output saveEmployee(Input input);
}
public Output extends BaseObj{
private static final long serialVersionUID = -7096731222829800554L;
//some fields are there
}
public Input extends BaseObj{
private static final long serialVersionUID = -70967312228423800554L;
//some fields
}
public BaseObj implements Serializable{
}
Now Input class and Output class has some fields, which also extends from BaseObj and has a generated SerialVersion UID.
Now we have deployed this EJB on WebSphere Application Server 7.0. The Remote invocation of EJB works fine. It breaks when we do a deployment of the Client web app which calls the EJB or the
web application which has the EJB. We get the ClassCastException that the
com.test.ejb._EmployeeRemoteEJB_Stub incompatible with com.test.ejb.EmployeeRemoteEJB
When we restart the applications, it starts working again.
This is how we invoke the Remote EJB
//This JNDI name is configured for the remote EJB.
String REMOTE_LOOKUP_KEY = "empremotesvc";
String JNDI_PROVIDER_URL = "iiop://10.222.232.111:2809";
Properties props = new Properties();
props.put( Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
props.put( Context.PROVIDER_URL, JNDI_PROVIDER_URL );
Object lobj;
InitialContext ctx;
try{
ctx = new InitialContext( props );
lobj = ctx.lookup( REMOTE_LOOKUP_KEY );
EmployeeRemoteEJB empObjRemote = (EmployeeRemoteEJB) lobj;
return empObjRemote ;
}
catch( NamingException e ){
e.printStackTrace();
}
Can someone please let me know what is causing this issue? Please let me know if you need any further details.
You need to call PortableRemoteObject.narrow:
lobj = ctx.lookup( REMOTE_LOOKUP_KEY );
EmployeeRemoteEJB empObjRemote = (EmployeeRemoteEJB)
PortableRemoteObject.narrow(lobj, EmployeeRemoteEJB.class);
The problem does not always occur because JNDI caches resolved IOR-to-stub using the class loader of first client app to look up the EJB, so the first application to use the target EJB will work, but the second and subsequent will not unless they use PortableRemoteObject.narrow.

Resources