How to upgrade spring boot admin from 1.5 to 2.0 - spring-boot

Is there any reference guide for spring boot admin upgrade?
I have a legacy app that I need to upgrade from 1.5 to 2.0, but the entire API has changed & there is 0 info in the official reference guide. https://codecentric.github.io/spring-boot-admin/current/
For example, the main domain class now seems to be InstanceEvent, whereas it used to be 'Application'; but they hold completely different info.
Same with the class 'AbstractStatusChangeNotifier'; which now seems to use InstanceEvent & Spring webflux...
My more specific question is:
How can I get application info from spring boot admin 2.0?
I used to be able to do this; which now no longer exists in the api.
public class XXXMailNotifier extends AbstractStatusChangeNotifier {
#Override
protected void doNotify(ClientApplicationEvent event) {
try {
helper.setText(mailContentGenerator.statusChange(event), true);
} catch (IOException | MessagingException e) {
logger.error(e.getMessage());
}
}
String statusChange(ClientApplicationEvent event) throws IOException {
ImmutableMap.Builder<String, Object> content = ImmutableMap.<String, Object>builder()
.put("name", event.getApplication().getName())
.put("id", event.getApplication().getId())
.put("healthUrl", event.getApplication().getHealthUrl())
.put("managementUrl", event.getApplication().getManagementUrl())
.put("serviceUrl", event.getApplication().getServiceUrl())
.put("timestamp", DATE_TIME_FORMATTER.print(new LocalDateTime(event.getApplication().getInfo().getTimestamp())));

Well, if it might help anyone...
I looked in the code and found that I can get the info from the instance.registration object.
You can change the above in the below:
#Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
try {
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setSubject(format(subject, environment, pool, instance.getRegistration().getName(), event.getInstance().getValue()));
helper.setText(mailContentGenerator.statusChange(event, instance, getLastStatus(event.getInstance())), true);
public String statusChange(InstanceEvent event, Instance instance, String lastStatus) throws IOException {
Registration registration = instance.getRegistration();
ImmutableMap.Builder<String, Object> content = ImmutableMap.<String, Object>builder()
.put("name", registration.getName())
.put("id", instance.getId().getValue())
.put("healthUrl", registration.getHealthUrl())
.put("managementUrl", registration.getManagementUrl())
.put("serviceUrl", registration.getServiceUrl())
.put("timestamp", DATE_TIME_FORMATTER.print(new LocalDateTime(instance.getStatusTimestamp())));

Related

How to enable Spring Boot to display a list of files under a directory

I have a folder structure /data/reports on a file system, which contains all reports.
How can I configure a SpringBoot application to serve the contents of this file sytem.
Currently I have tried few options, but none working
#Configuration
#EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
#Value(value = "${spring.resources.static-locations:#{null}}")
private String fileSystem;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/data/reports/**")
.addResourceLocations(fileSystem)
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
and in application.properties I have defined
spring.resources.static-locations=file:///data/reports
server.servlet.jsp.init-parameters.listings=true
But in both cases, when I try
http://host:port/application/data/reports
I'm getting 404
What am I missing ?
Based on the suggestions given, I realized that one mistake I'm doing is to access the reports via
http://host:port/application/data/reports
instead of
http://host:port/data/reports
if I use application in the request, those calls will go through RequestDispatcher and will try to find for a matching RequestMapping, which does not exist. I think I'm convinced so far.
But the problem I'm seeing now is, I'm getting SocketTimeoutException while trying to read from the resource listed in the URL. I had put some breakpoints in Spring source "ResourceHttpMessageConverter.java"
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try {
InputStream in = resource.getInputStream(); //It is timing out here
try {
StreamUtils.copy(in, outputMessage.getBody());
}
catch (NullPointerException ex) {
// ignore, see SPR-13620
}
The resource is a small text file with 1 line "Hello World". Yet it is timing out.
The resource in the above class is a FileUrlResource opened on file:///c:/data/reports/sample.txt
On the other hand, I tried to read that resource as
File file = new File("c:/data/reports/sample.txt");
System.out.println(file.exists());
URL url = file.toURI().toURL();
URLConnection con = url.openConnection();
InputStream is = con.getInputStream(); //This works
Thanks

Apache Camel CXF set Transport Properties

Im struggling with the Camel SXF Component. I need it to not use chunked encoding, but I do not find the correct way to set the Parameter.
According to the Apache CXF Docs(http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html) there should be a Parameter called "AllowChunking", but I had no luck when trying to using it. I tried this
.to("cxf:bean:pdsEndpointBean?loggingFeatureEnabled=true&properties.AllowChunking=false")
and this
#Bean
public CxfEndpoint pdsEndpointBean() {
CxfEndpoint cxfEndpoint = new CxfEndpoint();
cxfEndpoint.setAddress(endpoint);
cxfEndpoint.setEndpointName("foo");
cxfEndpoint.setWsdlURL("bar");
cxfEndpoint.setServiceClass(foo);
HashMap<String, Object> properties = new HashMap<>();
properties.put("AllowChunking",false);
cxfEndpoint.setProperties(properties);
return cxfEndpoint;
}
Can anyone help me out ? Thanks a lot :)
Using Camel 3.0.1
Try using CxfEndpointConfigurer like this:
cxfEndpoint.setCxfEndpointConfigurer(new CxfEndpointConfigurer() {
#Override
public void configure(final AbstractWSDLBasedEndpointFactory abstractWSDLBasedEndpointFactory) {
}
#Override
public void configureClient(final Client client) {
((HTTPConduit)client.getConduit()).getClient().setAllowChunking(false);
}
#Override
public void configureServer(final Server server) {
}
});
And always specify version of your camel

Olingo with Spring Boot

I am using this tutorial and it works for a simple java web application. Now I want to convert it to Spring Boot. I remove the web.xml and add the following two annotations to DemoServlet
#RestController
public class DemoServlet extends DispatcherServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(DemoServlet.class);
#RequestMapping("/DemoService.svc/*")
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
try {
// create odata handler and configure it with CsdlEdmProvider and Processor
OData odata = OData.newInstance();
ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>());
ODataHttpHandler handler = odata.createHandler(edm);
handler.register(new DemoEntityCollectionProcessor());
// let the handler do the work
handler.process(req, resp);
} catch (RuntimeException e) {
LOG.error("Server Error occurred in ExampleServlet", e);
throw new ServletException(e);
}
}
}
I also change the HTTPServlet to DispatcherServlet.
Now I am only able to access one end point. i.e.
http://localhost:8080/DemoService.svc/
The metadata end point is not working. It returns the service document instead of xml content.
http://localhost:8080/DemoService.svc/$metadata
Can somebody explain what is going on here?
user the below code for the process method.
handler.process(new HttpServletRequestWrapper(request) {
// Spring MVC matches the whole path as the servlet path
// Olingo wants just the prefix, ie upto /odata, so that it
// can parse the rest of it as an OData path. So we need to override
// getServletPath()
#Override
public String getServletPath() {
return "/DemoService.svc";
}
}, response);
You can create a #Configuration and Map your servlet in it like the following
#Bean
public ServletRegistrationBean odataServlet() {
ServletRegistrationBean odataServRegstration = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(),
"/DemoService.svc/*");
Map<String, String> initParameters = new HashMap<>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory",
"com.metalop.code.samples.olingo.springbootolingo2sampleproject.utils.JPAServiceFactory");
odataServRegstration.setInitParameters(initParameters);
return odataServRegstration;
}
Add the following after the handler.register call:
req.setAttribute("requestMapping", "/DemoService.svc");
The best implementation of olingo2 and spring-boot can be found here. I would suggest to take a look at this repository, it is very straight forward and easy.

Custom JASPIC on WebSphere error message

Though similar, the specific problem I have is not addressed in Use JASPIC auth module on WebSphere 8.5
I am getting the following error message:
SECJ8027E: The path and name of file where JASPI persistent registrations are stored must be specified using property com.ibm.websphere.jaspi.configuration.
I can set the custom property in the administration to some existing folder but I wanted to make sure that is the right approach or if there is some step I was missing.
Note I am specifically using the "embedded in application" approach rather than a server installed JASPIC module so I have something like this
#WebListener
public class JaspicInitializer implements
ServletContextListener {
#Override
public void contextInitialized(final ServletContextEvent sce) {
final Map<String, String> options = new HashMap<>();
AuthConfigFactory.getFactory()
.registerConfigProvider(AuthModuleConfigProvider.class.getName(), options, "HttpServlet", null, null);
}
}
I had the error on both WebSphere 8.5.5.11 and 9.0.0.3
From #Uux comment, I changed the way I do the registration so it no longer give the error.
#WebListener
public class JaspicInitializer implements
ServletContextListener {
private String registrationID;
#Override
public void contextDestroyed(final ServletContextEvent sce) {
AuthConfigFactory.getFactory().removeRegistration(registrationID);
}
#Override
public void contextInitialized(final ServletContextEvent sce) {
final ServletContext context = sce.getServletContext();
registrationID = AuthConfigFactory.getFactory()
.registerConfigProvider(new AuthModuleConfigProvider(), "HttpServlet",
context.getVirtualServerName() + " " + context.getContextPath(), "JEE Sample");
}
}
Also WebSphere Global Security needs to be configured with
Enable application security
Enable Java Authentication SPI (JASPI)

Get resteasy servlet context without annotation params

Quick project explanation: We have a built application based on JSF2 + Spring with Dynamic data sources. The data reference control is made with a spring-config:
<bean id="dataSource" class="com.xxxx.xxxx.CustomerRoutingDataSource">
....
and a class (referenced above):
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return CustomerContextHolder.getCustomerType();
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
the CustomerContextHolder called above is as follows:
public class CustomerContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
String manager = (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("dataBaseManager");
if (manager != null) {
contextHolder.set(manager);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("dataBaseManager", null);
} else {
String base = (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("currentDatabBase");
if (base != null)
contextHolder.set(base);
}
return (String) contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
The problem is that the last guy is calling FacesContext.getCurrentInstance() to get the servlet context. Just to explain, it uses the session Attribute dataBaseManager to tell which base it should use.
For the actual solution it was working fine, but with the implementation of a RESTEASY web service, when we make a get request the FacesContext.getCurrentInstance() is obviously returning null and crashing.
I searched a lot and could not find a way of getting the servlet-context from outside of the #GET params. I would like to know if is there any way of getting it, or if there is another solution for my dynamic datasource problem.
Thanks!
Like magic and probably not much people know.
I searched deep into the Resteasy documentation, and found a part of springmvc plugin that comes with the resteasy jars, that has a class called RequestUtil.class.
With that I was able to use the method getRequest() without the "#Context HttpServletRequest req" param.
Using that I was able to set the desired database on the request attributes, and from another thread (called by spring) get it and load the stuff from the right place!
I'm using it for a week now and it works like a charm. Only thing that I needed to do is change the determineLookupKey() above to this:
#Override
protected String determineCurrentLookupKey() {
if (FacesContext.getCurrentInstance() == null) {
//RESTEASY
HttpServletRequest hsr = RequestUtil.getRequest();
String lookUpKey = (String) hsr.getAttribute("dataBaseManager");
return lookUpKey;
}else{
//JSF
return CustomerContextHolder.getCustomerType();
}
}
Hope this helps other people!
Thiago

Resources