I have been using the dart:route api for serving static files but I noticed that there is a core library called http_server that contains helper classes and functions for dart:io HttpServer.
Of particular interest to me is the class VirtualDirectory which, according to the docs, takes a String Object for the static content of directory and then you call the method serve()
var virtualDirectory = new VirtualDirectory('/var/www/');
virtualDirectory.serve(new HttpServer('0.0.0.0', 8080));
This doesn't work as there is no constructor for HttpServer - at least not in current versions.
virtualDirectory.serve(HttpServer.bind('0.0.0.0', 8080));
Which is how I have been instantiating a server also fails since virtualDirectory.serve() doesn't take a Future<HttpServer> and finally:
virtualDirectory.serve(HttpServer.bind('0.0.0.0', 8080).asStream());
also fails with
The argument type 'Stream' cannot be assigned to the parameter type
'Stream'
So how do I connect a VirtualDirectory to a Server? There are no examples that I can find online and the VirtualDirectory source code does not make it clear. I would RTFM if I could FTFM. Links are fine as answers.
The VirtualDirectory can work from inside the Future returned by HttpServer.bind. You can create a static file web server by using the following five lines of code:
HttpServer.bind('127.0.0.1', 8888).then((HttpServer server) {
VirtualDirectory vd = new VirtualDirectory('../web/');
vd.jailRoot = false;
vd.serve(server);
});
You can make it more sophisticated by parsing the URI and pulling out service requests prior to serving out files.
import 'dart:io';
import 'package:http_server/http_server.dart';
main() {
handleService(HttpRequest request) {
print('New service request');
request.response.write('[{"field":"value"}]');
request.response.close();
};
HttpServer.bind('127.0.0.1', 8888).then((HttpServer server) {
VirtualDirectory vd = new VirtualDirectory('../web/');
vd.jailRoot = false;
server.listen((request) {
print("request.uri.path: " + request.uri.path);
if (request.uri.path == '/services') {
handleService(request);
} else {
print('File request');
vd.serveRequest(request);
}
});
});
}
Related
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.
I've been wanting to check the location of my Generator and use streams to check if the location is valid.
The idea was as follows;
public Generator getGeneratorFromLocation(final Location location) {
for (List<Generator> generator : playerGeneratorMap.values()) {
for (Generator generator1 : generator) {
if (generator1.getGenLocation().equals(location)) {
return generator1;
}
}
}
return null;
}
I'm wanting to return a Generator from this using streams instead to try and learn more ways of doing it.
Current map:
public final Map<UUID, List<Generator>> playerGeneratorMap = new HashMap<>();
Any help would be greatly appreciated.
You can use AtomicRef object to init a retVal and then assign the wanted Generator to it in the lambda expression because regular vars can't be assigned in lambdas, only final or effectivly final can be used inside arrow functions.
This function should solve the problem :)
public Generator getGeneratorFromLocation(final Location location) {
AtomicReference<Generator> retVal = new AtomicReference<>(null);
playerGeneratorMap.values().stream().forEach(generators -> {
generators.forEach(generator -> {
if (generator.getLocation().equals(location)) {
retVal.set(generator);
}
});
});
return retVal.get();
}
By the way, streams are unnecessary because you have Collection.forEach instead of Stream.forEach, streams are used for more 'exotic' types of iterations like, filter, anyMatch, allMatch, reduce and such functionalities, you can read about Streams API on Oracle's website,
I'll link in the docs for you for future usage, important for functional proggraming.
I am using a software framework which on its part uses log4j-2.7 (I can't update the jars of the framework).
I have written a third party library which provides a RewritePolicy to re-format the log messages. The library uses log4j-2.7 as well.
Within the framework I do some loggings with MapMessage. However, the rewrite policy receives them as SimpleMessage or some other types of Message, but not as MapMessage.
Here is code examples from framwork:
var mapMessage = new MapMessage()
mapMessage.put("first", "first")
mapMessage.put("second", "second")
logger.info(mapMessage)
And here the rewrite method of the RewritePolicy:
#Override
public LogEvent rewrite(LogEvent source) {
final Message modifiedMessage;
Message origMessage = source.getMessage();
if (origMessage instanceof MapMessage) {
Map<String, Object> map = new HashMap<String, Object>(((MapMessage) origMessage).getData());
modifiedMessage = new SimpleMessage(createStringMessage((HashMap<String, Object>) map));
} else {
modifiedMessage = origMessage;
}
LogEvent modifiedLogEvent = new Log4jLogEvent.Builder(source).setMessage(modifiedMessage).build();
return modifiedLogEvent;
}
My problem was due to the fact that the sofware platform mentioned in my post did migrate from log4j 1 to 2, and as explained here https://logging.apache.org/log4j/2.x/manual/migration.html, I had to change the package from org.apache.log4j to org.apache.logging.log4j.
IoT modules can be created from the environment using :
ModuleClient.CreateFromEnvironmentAsync(settings)
However, there does not seem to be an equivalent method for devices. For now, I am setting the device connection string in the program to test it out, but is there a better way to read teh connection string from iotedge/config.yaml for all the edge devices deployed out there?
Methods to do so for .NET and python would be appreciated.
You can use a yaml parse library to deserialize the document, such as YamlDotNet. In fact, you can refer to YamlDocument in iot edge. But in the class, it does not provide a method to get the key value. Please refer to following code.
public class YamlDocument
{
readonly Dictionary<object, object> root;
public YamlDocument(string input)
{
var reader = new StringReader(input);
var deserializer = new Deserializer();
this.root = (Dictionary<object, object>)deserializer.Deserialize(reader);
}
public object GetKeyValue(string key)
{
if(this.root.ContainsKey(key))
{
return this.root[key];
}
foreach(var item in this.root)
{
var subItem = item.Value as Dictionary<object, object>;
if(subItem != null && subItem.ContainsKey(key))
{
return subItem[key];
}
}
return null;
}
}
And then you can get the device connection string from the config.yaml. If you use python, you can import yaml library to analysis the file.
StreamReader sr = new StreamReader(#"C:\ProgramData\iotedge\config.yaml");
var yamlString = sr.ReadToEnd();
var yamlDoc = new YamlDocument(yamlString);
var connectionString = yamlDoc.GetKeyValue("device_connection_string");
Console.WriteLine("{0}", connectionString);
To get the config file from the host, add the following to the docker deployment file. Note that the source file is config1.yaml which is the same as config.yaml except that it has read permissions for everyone not just root.
"createOptions": "{\"HostConfig\":{\"Binds\":[\"/etc/iotedge/config1.yaml:/app/copiedConfig.yaml\"]}}"
With the above line in place, the copiedConfig.yaml file can be used in the container, along with #Michael Xu's parsing code to derive teh connection string.
Long term, one may want to use the device provisioning service anyway but hope this helps for folks using device conenction strings for whatever reason..
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) {}