Spring Integration - List file names being downloaded via a RemoteFileTemplate - spring-integration-dsl

I am downloading files via Sftp using a Spring Integration RemoteFileTemplate. How do I perform some processing on each file name that is being downloaded? I see that the line
.log(LoggingHandler.Level.INFO, "sftp.inbound", Message::getHeaders)
logs the file names but I need the file names available directly.
All I need to do is write the downloaded file names as a list into a POJO for passing as a response to a later process. My code is attached below.
`
#Configuration
#EnableIntegration
public class SftpInboundFlowIntegrationConfig {
private static final Logger log = LoggerFactory.getLogger(SftpInboundFlowIntegrationConfig.class);
private String sftpRemoteDirectory = "/";
#Bean
public SessionFactory<ChannelSftp.LsEntry> inboundSftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost("localhost");
factory.setPort(2222);
factory.setUser("local");
factory.setPassword("local");
factory.setAllowUnknownKeys(true);
return new CachingSessionFactory<>(factory);
}
#Bean
public IntegrationFlow sftpInboundFlow(RemoteFileTemplate<ChannelSftp.LsEntry>
inboundRemoteFileTemplate) {
return e -> e
.log(LoggingHandler.Level.INFO, "sftp.inbound", Message::getPayload)
.log(LoggingHandler.Level.INFO, "sftp.inbound", Message::getHeaders)
.handle(
Sftp.outboundGateway(inboundRemoteFileTemplate, AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
.localDirectory(new File("c:/tmp"))
);
}
#Bean
public RemoteFileTemplate<ChannelSftp.LsEntry> inboundRemoteFileTemplate(SessionFactory<ChannelSftp.LsEntry> inboundSftpSessionFactory) {
RemoteFileTemplate<ChannelSftp.LsEntry> template = new SftpRemoteFileTemplate(inboundSftpSessionFactory);
template.setRemoteDirectoryExpression(new LiteralExpression(sftpRemoteDirectory));
template.setAutoCreateDirectory(true);
template.afterPropertiesSet();
template.setUseTemporaryFileName(false);
return template;
}
}
`

Sorry all. I was trying to accomplish this in the wrong area of code. I realized that when I call my outbound gateway to download the files, sftpOutboundGateway.mget("/"); that it returns the list of files downloaded, which is what I needed.

Related

How to add pdfTemplateResolver for Thymeleaf configuration in my jhipster application

I have an application generated with jhipster, it makes use of Thymeleaf to render email templates. All works nice.
I want to add a pdf generator for which i will to use the thymeleaf templates. So i would like to add a ClassLoaderTemplateResolver. I have this:
#Configuration
public class ThymeleafConfiguration {
#SuppressWarnings("unused")
private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
#Bean
#Description("Thymeleaf template resolver serving HTML 5 emails")
public ClassLoaderTemplateResolver emailTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML5");
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
emailTemplateResolver.setOrder(1);
return emailTemplateResolver;
}
#Bean
#Description("Thymeleaf template resolver serving HTML 5 emails")
public ClassLoaderTemplateResolver pdfTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("pdf/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML5");
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
emailTemplateResolver.setOrder(1);
return emailTemplateResolver;
}
}
Which makes it possible to put the templates in a different directory.
What is unclear to me is how do you make sure i get the pdf template resolver for pdf's and the email template resolver for generating emails?
Ok it seems both get picked up automatically. Not sure what will happen when a template name is in both places. To avoid that i could add two beans like this:
#Bean
public SpringTemplateEngine emailTemplateEngine() {
final SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<TemplateResolver> templateResolvers = new HashSet<TemplateResolver>();
templateResolvers.add(emailTemplateResolver());
engine.setTemplateResolvers(templateResolvers);
return engine;
}
#Bean
public SpringTemplateEngine pdfTemplateEngine() {
final SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<TemplateResolver> templateResolvers = new HashSet<TemplateResolver>();
templateResolvers.add(pdfTemplateResolver());
engine.setTemplateResolvers(templateResolvers);
return engine;
}

Jenkins API to retrieve a build log in chunks

For a custom monitoring tool I need an API (REST) to fetch the console log of a Jenkins build in chunks.
I know about the /consoleText and /logText/progressive{Text|HTML} APIs, but the problem with this is that sometimes, our build logs get really huge (up to a few GB). I have not found any way using those existing APIs that avoids fetching and transferring the whole log in one piece. This then normally drives the Jenkins master out of memory.
I already have the Java code to efficiently fetch chunks from a file, and I have a basic Jenkins plugin that gets loaded correctly.
What I'm missing is the correct extension point so that I could call my plugin via REST, for example like
http://.../jenkins/job/<jobname>/<buildnr>/myPlugin/logChunk?start=1000&size=1000
Or also, if that is easier
http://.../jenkins/myPlugin/logChunk?start=1000&size=1000&job=<jobName>&build=<buildNr>
I tried to register my plugin with something like (that code below does not work!!)
#Extension
public class JobLogReaderAPI extends TransientActionFactory<T> implements Action {
public void doLogChunk(StaplerRequest req, StaplerResponse rsp) throws IOException {
LOGGER.log(Level.INFO, "## doLogFragment req: {}", req);
LOGGER.log(Level.INFO, "## doLogFragment rsp: {}", rsp);
}
But I failed to find the right encantation to register my plugin action.
Any tips or pointers to existing plugins where I can check how to register this?
This was indeed more simple than I expected :-) It as always: once one understands the plugin system, it just needs a few lines of code.
Turns out all I needed to do was write 2 very simple classes
The "action factory" that get's called by Jenkins and registers an action on the object in question (in my case a "build" or "run"
public class ActionFactory extends TransientBuildActionFactory {
public Collection<? extends Action> createFor(Run target) {
ArrayList<Action> actions = new ArrayList<Action>();
if (target.getLogFile().exists()) {
LogChunkReader newAction = new LogChunkReader(target);
actions.add(newAction);
}
return actions;
}
The class the implements the logic
public class LogChunkReader implements Action {
private Run build;
public LogChunkReader(Run build) {
this.build = build;
}
public String getIconFileName() {
return null;
}
public String getDisplayName() {
return null;
}
public String getUrlName() {
return "logChunk";
}
public Run getBuild() {
return build;
}
public void doReadChunk(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {

JMX in SpringBoot Application. Not able to get MBean for classes that implement an Interface

I Have a SpringBoot application . I have used the Java config in the entire application and it is working fine.
Now I have a reqirement to configure JMX for the same.
After going through some tutorials I found that I need to give the following configuration in my spring boot to have the JMX enabled.
#Bean
public MetadataNamingStrategy getNamingStrategy() {
MetadataNamingStrategy strategy = new MetadataNamingStrategy();
strategy.setAttributeSource(new AnnotationJmxAttributeSource());
return strategy;
}
#Bean
public MetadataMBeanInfoAssembler getMbeanInfoAssembler() {
return new MetadataMBeanInfoAssembler(new AnnotationJmxAttributeSource());
}
#Bean
public MBeanExporter getExporter() {
MBeanExporter exporter = new MBeanExporter();
exporter.setAutodetect(true);
exporter.setNamingStrategy(getNamingStrategy());
exporter.setAssembler(getMbeanInfoAssembler());
return exporter;
}
After doing this , I am able to see the classes (and their attributes, and operations) in JMX console (on HCP).
But for the classes that implement an interface ,I am not able to see them on JMX console.
Example:
I have an interface TestInterface2.
package com.test.example;
public interface TestInterface2 {
public String simpleTest2();
}
The implementation of the interface goes below.
#Component("testInterfaceImpl2")
#ManagedResource(objectName = "com.test.example:type=TestInterfaceImpl2", description = "TestInterface2 Desc")
public class TestInterfaceImpl2 implements TestInterface2 {
#Override
public String simpleTest2() {
return "Simple Test";
}
}
I tried this. It is not working.
But the following am able see in JMX console
#Component
#ManagedResource(
objectName = "com.test.example:type=FormHandler",
description = "Form Handler implementation")
public class FormHandler {
#implementation details here
}
Can someone suggest the best approach for doing this or why am i not able to get the interface implementations as MBean .

Making business domain objects available to Jersey Servlet Context in embedded Jetty server

Using the following dependencies (Gradle):
org.glassfish.jersey.containers:jersey-container-servlet:2.22.2
org.eclipse.jetty:jetty-servlet:9.3.2.v20150730
I have an embedded Jetty server, with a Jersey servlet container... something like this ...
package mypkg.rest.jersey;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import se.transmode.tnm.alarm.api.AlarmRetrieval;
import mypkg.rest.RestServer;
import mypkg.rest.jersey.serviceImpl.ModelAdapter;
public class JerseyBasedRestServer implements RestServer {
public static final int INITIALIZE_ON_USE = 0;
private Server server;
private final ServletContextHandler context;
private final ServletHolder servlet;
private final ModelAdapter modelAdapter;
public JerseyBasedRestServer(BusinessObjects businessObjects) {
this.modelAdapter = new ModelAdapter(businessObjects); //I want this instance to somehow be available for my ServletContainer to use.
context = new ServletContextHandler(ServletContextHandler.SESSIONS);
servlet = context.addServlet(ServletContainer.class, "/*");
servlet.setInitOrder(INITIALIZE_ON_USE);
servlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage.jersey.generated.api.service");
servlet.setInitParameter(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
context.setContextPath("/");
}
private void startServlet() {
try {
servlet.start();
servlet.initialize();
} catch (Exception e) {
log.error("Failed to initialize servlet. {}", e.getMessage());
}
}
#Override
public void init(int port) {
server = new Server(port);
server.setHandler(context);
try {
server.start();
server.join();
startServlet();
} catch (Exception e) {
log.error("Failed to start jetty server for rest interface");
} finally {
server.destroy();
}
}
The Jersey Container will run server code and model generated using the Swagger code-gen tool
https://github.com/swagger-api/swagger-codegen#getting-started
which delivers the generated model, JacksonJsonProvider, and a RestApi class:
package mypackage.jersey.generated.api.service
Path("/")
public class RestApi {
private final RestApiService delegate = new RestApiServiceImpl(); //Integration point of the generated code
#GET
#Path("/list/")
#Consumes({ "application/json" })
#Produces({ "application/json" })
public Response retrieveAlarmList(#Context SecurityContext securityContext) throws NotFoundException {
return delegate.retrieveAlarmList(securityContext);
}
}
To integrate the generated code we are left to implement RestApiServiceImpl ourselves.
The ModelAdapter's job is to convert our business objects to the generated rest model.
So the question is how do I make the instance of the adapter of our business objects, in this case ModelAdapter, which lies outside the context of the Jersey servlet context, available to the RestApi class, or rather the RestApiServiceImpl?
I kind of understood from reading the past 24 hours that I need to use some sort of Context Dependency Injection either through Jetty, Jersey, or some other library (Weld seems to appear a lot), and have tried various combinations of #Inject, #Context, etc etc, but have come to the conclusion that I have no clue what I am actually doing... I'm not even sure I understand enough about the situation to phrase my question correctly.
More info can be made available on request.
Any help is appreciated.
EDIT: added a link here to https://github.com/englishbobster/JersetAndJetty
using #peeskillets suggestions, but still not working.
First thing you need to make DI work, is an AbstractBinder. This is where you will make your objects available to be injected.
class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(modelAdapter).to(ModelAdapter.class);
}
}
Then you need to register the binder with Jersey. The easiest way is to register in Jersey's ResourceConfig. In your case, you are not using one. You are configuring everything in the "web.xml". For that, you should take a look at this post.
If you want to change your configuration to use a ResourceConfig, which personally I'd rather use, you can do this
package com.some.pkg;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("mypackage.jersey.generated.api.service");
property(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
register(new Binder());
}
}
Then to configure it with Jetty, you can do
servlet.setInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS,
"com.some.pkg.JerseyConfig");
Now you can get rid of those other two init-params, as you are configuring it inside the ResourceConfig.
Another way, without any init-params, is to do
ResourceConfig config = new JerseyConfig();
ServletHolder jerseyServlet = new ServletHolder(ServletContainer(config));
context.addServlet(jerseyServlet, "/*");
See full example of last code snippet, here.
Now you can just inject the ModelAdapter pretty much anywhere within Jersey
In a field
#Inject
private ModelAdapter adapter;
Or in a contructor
#Inject
public RestApi(ModelAdapter adapter) {
this.adapter = adapter;
}
Or method parameter
#GET
public Response get(#Context ModelAdapter adapter) {}

Customize stream playback paths in Red5 to access file from a shared machine

I have installed Red5 server. I have created a custom application same as oflaDemo. I am able to play videos in /streams folder of my application, my application name is demo.I want to change the directory RED5_HOME/demo/webapps/streams from which my application is accessing videos, to a folder in a shared machine. I am able to change to a directory in local machine, for example "c:\streams". I have achieved this using CustomFileNameGenerator implementing IStreamFilenameGenerator . But I am not able to access a shared folder. Here is my CustomFileNameGenerator class
import java.io.File;
import org.apache.log4j.Logger;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.stream.IStreamFilenameGenerator;
public class CustomFilenameGenerator implements IStreamFilenameGenerator {
Logger log = Logger.getLogger(CustomFilenameGenerator.class);
/** Path that will store recorded videos. */
/public String recordPath = "recordedStreams/";/
/** Path that contains VOD streams. */
public String playbackPath;
/** Set if the path is absolute or relative */
public Boolean resolvesAbsolutePath;
public String generateFilename(IScope scope, String name, GenerationType type) {
// Generate filename without an extension.
return generateFilename(scope, name, null, type);
}
public String generateFilename(IScope scope, String name, String extension, GenerationType type) {
String filename = null;
if (type == GenerationType.PLAYBACK)
{
filename = playbackPath + name;
}
log.info("file Name " + filename);
if (extension != null)
// Add extension
filename += extension;
log.info("Extension and file name " + filename);
return filename;
}
public boolean resolvesToAbsolutePath()
{
log.info("resolvesAbsolutePath" + resolvesAbsolutePath);
return resolvesAbsolutePath;
}
public void setPlaybackPath(String playbackPath) {
this.playbackPath = playbackPath;
}
public void setResolvesAbsolutePath(Boolean resolvesAbsolutePath) {
this.resolvesAbsolutePath = resolvesAbsolutePath;
}
}
Following are properties in my red5-web.properties file:
webapp.contextPath=/demo
webapp.virtualHosts=*, 192.168.1.20, 192.168.1.20:8088, 127.0.0.1:8088, 192.168.1.20:1935
playbackPath=C://streams/
resolvesAbsolutePath=true
Following is the bean definition in my red5-web.xml file
<bean id="streamFilenameGenerator" class="com.abhinow.demo.CustomFilenameGenerator" >
<property name="playbackPath" value="${playbackPath}" />
<property name="resolvesAbsolutePath" value="${resolvesAbsolutePath}" />
</bean>
The above given code is working fine and I am able to playback video in C:\streams folder, but when I have changed the playback path to a shared folder like
/192.168.1.20/streams
it is not working. I am using windows computer. I have also tried by mapping shared folder /192.168.1.20/streams to a network drive using map network drive feature in windows and gave name to that drive as Z:. Then I have tried by giving the path
Z://streams
Now also it is not working.
Any one please help where I am getting it wrong. I have been struggling on it for two days. Please help me to solve this issue.
Thanks a lot in advance.
Did you debug if
generateFilename
is ever called in your application?
The first thing might be to add some log.debug statements to the code and having a look at the red5.log (or std.out).
Sebastian
Try:
public String generateFilename(IScope scope, String name, String extension, GenerationType type) {
...
return playbackPath+filename;
}

Resources