How to access package from another service within the same parent - spring

I have created 5 microservices, all having the same parent in pom. I am trying to access a package from one child service core-service into another data-processor.
Below is my data-processor code which is trying to access com.itqe.tdm.core.model.Customer from core-service
package com.itqe.dataprocessor.util;
import org.springframework.batch.item.excel.RowMapper;
import org.springframework.batch.item.excel.support.rowset.RowSet;
import org.springframework.stereotype.Component;
import com.itqe.tdm.core.model.Customer;
#Component
public class RowMapperImpl implements RowMapper<Customer> {
#Override
public Customer mapRow(RowSet rs) throws Exception {
if (rs == null || rs.getCurrentRow() == null) {
return null;
}
Customer cust = new Customer();
cust.setFirstName(rs.getColumnValue(0));
cust.setLastName(rs.getColumnValue(1));
return cust;
}
}
When I try to build the code mvn clean install I am getting the following error
package com.itqe.tdm.core.model does not exist
I already did mvn install on the parent service as well as core-service. The jar is present in my m2. I added the dependency in the pom of data-processor
<dependency>
<groupId>com.itqe</groupId>
<artifactId>core-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Could someone please help me understand what am I missing here?
Appreciate the help.
Parent pom.xml
<modules>
<module>config-server</module>
<module>registry-service</module>
<module>gateway-service</module>
<module>core-service</module>
<module>data-processor</module>
</modules>

Related

Spring boot and Swagger url and startup questions

I come from programming in c# and now I have to create a couple of Rest Apis in Spring Boot.
Everything is working ok and I can show the API in Swagger with springfox-swagger-ui
But I have two questions that I could not find in Internet
Is there any way to show the url ui in the console app with server, port, etc?
Is there any way to open the swagger url everytime I run the app in the localhost?
Thanks
Spring boot version
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
I know it's not the question you're asking, but springfox is currently having issues with newer versions of spring. The spring version you're using is still working but as of 2.6 there are bugs, and it looks like the project is not well maintained. Since you're at the beginning of the project, switching is not too hard. You could move to springdocs for example (for migration: https://springdoc.org/#migrating-from-springfox).
With respect to opening a url, there are some good solutions mentioned here: How to open the default webbrowser using java . You could make your swagger url a property and have swagger configure it accordingly, then you can reuse the property to call the url on run-time. If you want to differentiate between environments I'd suggest use profiles. Only open the url in the browser if you start the app on dev environment, and not on prod is then specified by using #Profile("dev"). Create a commandline/application runner with the profile annotation (https://www.tutorialspoint.com/spring_boot/spring_boot_runners.htm), and call the url from there.
That said, combining it gives:
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import java.awt.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
#Profile("dev")
#Component
public class SwaggerRunner implements ApplicationRunner {
#Value("${springdoc.swagger-ui.path}")
private String swaggerPath;
#Override
public void run(ApplicationArguments args) throws Exception {
log("\nWelcome to Multi Brow Pop.\nThis aims to popup a browsers in multiple operating systems.\nGood luck!\n");
final String swaggerUrl = "http://localhost:8000/" + swaggerPath;
log("We're going to this page: " + swaggerUrl);
String myOS = System.getProperty("os.name").toLowerCase();
log("(Your operating system is: " + myOS + ")\n");
try {
if (Desktop.isDesktopSupported()) { // Probably Windows
log(" -- Going with Desktop.browse ...");
Desktop desktop = Desktop.getDesktop();
desktop.browse(new URI(swaggerUrl));
} else { // Definitely Non-windows
Runtime runtime = Runtime.getRuntime();
if (myOS.contains("mac")) { // Apples
log(" -- Going on Apple with 'open'...");
runtime.exec("open " + swaggerUrl);
} else if (myOS.contains("nix") || myOS.contains("nux")) { // Linux flavours
log(" -- Going on Linux with 'xdg-open'...");
runtime.exec("xdg-open " + swaggerUrl);
} else
log("I was unable/unwilling to launch a browser in your OS :( #SadFace");
}
log("\nThings have finished.\nI hope you're OK.");
} catch (IOException | URISyntaxException eek) {
log("**Stuff wrongly: " + eek.getMessage());
}
}
private static void log(String log) {
System.out.println(log);
}
}
put springdoc.swagger-ui.path=/custom/path in your application.properties to change the path to your swagger-ui

Declarative services in OSGI

I have created a (very) simple test to determine how to send and receive events using Apache Felix.
This is my sender:
package be.pxl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import java.util.HashMap;
#Component(name = "be.pxl.Publisher", immediate = true)
public class Publisher {
EventAdmin admin;
#Activate
public void run(Object object) {
System.out.println("IN PUBLISHER");
Event event = new Event("event", new HashMap<String, Object>());
System.out.println("\tEVENT: " + event);
admin.postEvent(event);
System.out.println("\tADMIN: " + admin);
}
#Reference(name="be.pxl.admin", service = EventAdmin.class)
protected void setEventAdmin(EventAdmin admin) {
this.admin = admin;
}
}
This is my receiver:
package be.pxl;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import java.util.Dictionary;
import java.util.Hashtable;
#Component(name = "be.pxl.Subscriber", immediate = true)
public class Subscriber implements EventHandler {
private BundleContext context;
#Activate
public void run(Object object) {
System.out.println("IN SUBSCRIBER");
System.out.println("\tIN RUN METHOD");
String[] topics = new String[]{"event"};
Dictionary props = new Hashtable();
props.put(EventConstants.EVENT_TOPIC, topics);
System.out.println("\t\tCONTEXT: " + context);
context.registerService(EventHandler.class.getName(), this, props);
System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
}
public void handleEvent(Event event) {
System.out.println("IN SUBSCRIBER");
String text = event.getProperty("text").toString();
System.out.println("\tEVENT CALLED: " + text);
}
#Reference(name="be.pxl.context", service=BundleContext.class)
protected void setBundleContex(BundleContext context) {
this.context = context;
}
}
This is the pom of my sender:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>be.pxl</groupId>
<artifactId>EventSender</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<version>1.3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<version>3.2.100.v20100503</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Vendor>SmartCampus</Bundle-Vendor>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>
be.pxl.*;version="1.0.0"
</Export-Package>
<Import-Package>
org.osgi.service.component.annotations
org.eclipse.osgi.service
org.osgi.core
org.osgi.service.event
</Import-Package>
<_dsannotations>*</_dsannotations>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Everything compiles fine. I create it using mvn clean package, then I install this jar file in my apache felix container and start it. However, nothing happens. Nothing get pritns out.
Thanks in advance!
You appear to be most of the way there! As you've identified, Event Admin uses a whiteboard model to receive events. The important thing is that you need to tell the whiteboard which topics you want to listen to, which you do.
%%% Update %%%
Event admin topic names use a hierarchy of tokens separated by / characters. When publishing an event you do so to a specific topic, for example foo/bar/baz. When receiving events the EventHandler will be called for topics that match its registered interest(s). These interests can either be for a specific topic, or they can end with a * to indicate a wildcard match. For example foo/bar/* would receive events sent to foo/bar/baz and events sent to foo/bar/fizzbuzz.
%%% Back to the original %%%
There are, however a couple of issues with your code:
Firstly:
#Reference(name="be.pxl.context", service=BundleContext.class)
protected void setBundleContex(BundleContext context) {
this.context = context;
}
This is not how you access the BundleContext for your bundle. If you do need a BundleContext then it should be injected as a parameter into your #Activate annotated method. A BundleContext should never be registered as a service (it represents your bundle's private access to the OSGi framework), and it would not surprise me to find that this reference is unsatisfied in your example. You don't actually need the BundleContext however because...
Secondly:
#Activate
public void run(Object object) {
System.out.println("IN SUBSCRIBER");
System.out.println("\tIN RUN METHOD");
String[] topics = new String[]{"event"};
Dictionary props = new Hashtable();
props.put(EventConstants.EVENT_TOPIC, topics);
System.out.println("\t\tCONTEXT: " + context);
context.registerService(EventHandler.class.getName(), this, props);
System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
}
This is not the right way to write an activate method (and as a result it may not be being called), nor should you be registering your component as a service here. When you make your class an #Component it will automatically be registered as a service using each directly implemented interface. This means that:
#Component(name = "be.pxl.Subscriber", immediate = true)
public class Subscriber implements EventHandler {
...
}
is already an OSGi EventHandler service!
You can add service properties to your component using the #Component annotation, or from the OSGi R7 release (due in a couple of months) using Component Property annotations. In this case you want to set your event.topics property like this:
#Component(property="event.topics=event")
You can then get rid of the activate method completely if you like.
Finally:
Event Admin is not a message queue, and your publisher is a one-shot send. Therefore if your publisher sends the event before the handler is fully registered then it will never receive the event. Consider making the publisher send periodic events, or be certain that the receiver starts before the publisher so that you see the message.
P.S.
It's not technically a problem, but I see that you're using version 2.4 of the maven-bundle-plugin. This is very old and the current released version of bnd is 3.5.0. The Bnd team have also started providing their own Maven plugins (such as the bnd-maven-plugin) that you might want to look at.

Microservice with Spring Boot

I'm working in Windows 7. I've Spring CLI v1.5.3.RELEASE installed. In a working directory, using command
spring init --build maven --groupId com.redhat.examples
--version 1.0 --java-version 1.8 --dependencies web
--name hola-springboot hola-springboot
I created holo-springboot app. Then navigated to hola-springboot directory,ran
$ mvn spring-boot:run
The application run. Going to http://localhost:8080, I do see Whitelabel error page. Whereafter, I tried to add helloworld fuctionality. That is, in the app, in the packeage com.example, I included the following java class.
#RestController
#RequestMapping("/api")
public class HolaRestController {
#RequestMapping(method = RequestMethod.GET, value = "/hola",
produces = "text/plain")
public String hola() throws UnknownHostException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
hostname = "unknown";
}
return "Hola Spring Boot de " + hostname;
}
}
Re-built from hola-springboot dircetory,
mvn clean package
I get build failure as at
https://pastebin.com/77Ru0w52
I'm unable to figure out. Could somebody help?
I'm following the book Microservices for Java Developers by Christian Posta, Chapter 2, available free at developers Redhat.
Looks like you are missing a dependency on spring boot starter web in your maven pom.xml file https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web/1.5.3.RELEASE.
Or you are not importing the classes correctly.
You are accessing http://localhost:8080 but you have defined a mapping in your rest controller "/hola". So you will have to access the url http://localhost:8080/hola as you do not have any default method in your rest controller.
BuildFailure shows that you have not given import statements in you Class. statements missing are the below
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.net.InetAddress;
import java.net.UnknownHostException;
include these and you will be fine.

How to make webjars version agnostic in spring mvc

I have followed the documentation on their website as described here
First of all I added the required path
<mvc:resources mapping="/webjars/**" location="/webjars/"/>
then I created a controller with the following
#ResponseBody
#RequestMapping("/webjarslocator/{webjar}/**")
public ResponseEntity locateWebjarAsset(#PathVariable String webjar, HttpServletRequest request) {
try {
String mvcPrefix = "/webjarslocator/" + webjar + "/"; // This prefix must match the mapping path!
String mvcPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String fullPath = assetLocator.getFullPath(webjar, mvcPath.substring(mvcPrefix.length()));
return new ResponseEntity(new ClassPathResource(fullPath), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Few dependencies were missing so I added in maven the following pom
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.28</version>
</dependency>
The above will import the following
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.core.io.ClassPathResource;
None of these has been imported from the external jar.
The error is: assetLocator cannot be resolved
EDIT: It could be that I need to create a filter rather than put it in a controller. Any thoughts on this?
The documentation is quite sparse, but you can create an instance of an asset locator with new WebJarAssetLocator().
Here's another solution, considering more recent versions of WebJar, in a similar vein for anyone else who stumbles upon this question. Using org.webjars.play.WebJarsUtil#locate(java.lang.String, java.lang.String) (which, in-turn, uses org.webjars.WebJarAssetLocator#getFullPath(java.lang.String, java.lang.String)) accepts the WebJar name (which acts as a scope) and the file without needing to provide a full path that necessitates including the version.

Invoke Struts Action Test from within a web app (JSF managed bean): TestCase.fname cannot be null

I have a Junit 3.8 test of a Struts 2 action that runs with no problems from my workspace (from eclipse: right click > run as > junit test).
For this, I use two plugins:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.1.8</version>
</dependency>
Here is the test class:
package com.myapp.user.my;
import org.apache.struts2.StrutsSpringTestCase;
import com.myapp.user.action.UserAction;
import com.opensymphony.xwork2.ActionProxy;
public class TestAccountActionUsingStrutsTestCase extends StrutsSpringTestCase {
public void testUserNameErrorMessage() throws Exception {
request.setParameter("userBean.userName", "Bruc");
request.setParameter("userBean.password", "test");
ActionProxy proxy = getActionProxy("/userAction");
UserAction userAction = (UserAction) proxy.getAction();
proxy.execute();
assertTrue("Problem There were no errors present in fieldErrors but there should have been one error present", userAction.getFieldErrors().size() == 1);
assertTrue("Problem field user.userName not present in fieldErrors but it should have been",
userAction.getFieldErrors().containsKey("userBean.userName") );
System.out.println("Finish 1 test.");
}
}
Next, I try to invoke this test, this time from within a web application (a JSF managed bean).
Here is my code for trying to do that (I'm calling the following runTest() method from a managed bean):
import java.util.List;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import com.myapp.user.my.TestAccountActionUsingStrutsTestCase;
public class CallStrutsActionExecuteThruTest {
public void runTest(){
System.out.println("CallStrutsActionExecuteThruTest.runTest() is executed.");
TestAccountActionUsingStrutsTestCase test = new TestAccountActionUsingStrutsTestCase();
JUnitCore jUnitCore = new JUnitCore();
Result result = jUnitCore.run(test);
List<Failure> list = result.getFailures();
for (Failure failure : list) {
System.out.println(failure.getMessage());
}
System.out.println("Test done!");
}
}
When I access the managed bean, I can see that runTest() is called. The first output CallStrutsActionExecuteThruTest.runTest() is executed. is printed to console. Strangely, the next outputs are not printed to console, although the debugger shows me they are executed.
Also, result.getFailures() returns a list with one element. As I said, its failure.getMessage() for some reason is not printed to console, but when I watch it in the debugger its value is TestCase.fname cannot be null.
* Even when I have only one method in my test class:
public void testTrue() throws Exception {
System.out.println("inside testTrue().");
assertTrue(true);
}
I still get the same results.
My questions are,
If I want to run the Struts action test from a JSF managed bean, am I using the Junit API correctly?
Why weren't the outputs that followed the first one printed to console?
How do I set TestCase.fname with a value? First I don't see a method in my test class to set this value. Second, from my understanding, fanme is the name of the test method in the test class that I want to call; and jUnitCore.run(test) should call all the test methods in the test class test, so how can I specify all these methods with only one fname parameter?
Download - you can download my project here. I use Maven, Eclipse, and deploy on Jboss 7.
I access the JSF managed bean by: http://localhost:8080/Struts2WithSpringDIIntegrationExampleJunitFromUI-1.0-SNAPSHOT/xhtml/hello.jsf
Struts2 tests don't work with raw parameters for some reason. Use parameterMap instead.
Map<String, String[]> parameterMap = new HashMap<String, String[]>();
parameterMap.put("userBean.userName", new String[]{"Bruc"});
parameterMap.put("userBean.password", new String[]{"test"});
StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest();
request.setupGetServletPath("/userAction");
request.setParameterMap(parameterMap);

Resources