setting micronaut configuration location - groovy

I have an existing groovy micronaut app I'm trying to change where it loads its config from. I don't understand what code to write so I can set the location of the micronaut configuration. I know you can use micronaut.config.files system variable or MICRONAUT_CONFIG_FILES environment variable, but this is a terrible idea because micronaut is built into grails and therefore every grails app you have running in tomcat will pick up the same config and crash.
Nor do I know where in the code to set the config file. There's an Application class with a run() method, but I don't know if this is only called during development, or whether it gets called when deploying in Tomcat. When setting the config in a Grails app, there is an Application class extending EnvironmentAware, and you can override setEnvironment, and load external configs there, but there is no hint of that for micronaut apps.
The micronaut doco says it can load a configuration from "application.{extension}", but it doesn't say what "application" is, or what directory it expects that in, or whether you can change the directory. Is "application" the value of micronaut.application.name in one's application.yml? I couldn't seem to get it to load based on that.
Then the documentation talks about loading from a PropertySource, which is fine and all, but doesn't tell you where you can put that code to load from a PropertySource. There is mention you can pass the PropertySource to ApplicationContext.run(xx), but in this app I inherited, there is no mention of ApplicationContext, and the micronaut documentation isn't very clear what I'm supposed to do with ApplicationContext. This app I've inherited has an Application class with a main() calling Micronaut.run() which apparently returns an ApplicationContext, but it's not clear if main() is called when running in Tomcat, or whether I should be calling run() on that, when it works as is, and I'm just trying to change where it loads its config.

The question is, how do I get my micronaut app to load its config from
where I tell it to, and not from micronaut.config.file system variable
location.
I don't think we have a specific feature in the framework that allows you to tell the framework to ignore micronaut.config.files. If you would like such a feature you can request it at https://github.com/micronaut-projects/micronaut-core/issues. If that is of interest I suggest you open it up for discussion at https://github.com/micronaut-projects/micronaut-core/discussions first.

You can load external config files, from a path not set as micronaut.config.files, in the main method of the Application class before running the application. Take a look at below class which accepts a config folder location as a system property demo.config.path(can be something else) and loads yaml config files from that folder:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import io.micronaut.context.env.PropertySource;
import io.micronaut.context.env.yaml.YamlPropertySourceLoader;
import io.micronaut.core.io.ResourceLoader;
import io.micronaut.core.io.file.DefaultFileSystemResourceLoader;
import io.micronaut.runtime.Micronaut;
public class Application {
private static final String PROP_CONFIG_LOCATION = "demo.config.path";
public static void main(String[] args) throws IOException {
if (System.getProperty(PROP_CONFIG_LOCATION) != null) {
List<PropertySource> propertySources = new ArrayList<>();
YamlPropertySourceLoader propertySourceLoader = new YamlPropertySourceLoader();
ResourceLoader resourceLoader = new DefaultFileSystemResourceLoader();
Files.newDirectoryStream(Paths.get(System.getProperty(PROP_CONFIG_LOCATION))).forEach(file -> {
String fileName = file.toString();
String fileNameWithoutExtension = fileName.substring(0, fileName.lastIndexOf('.'));
propertySourceLoader.load(fileNameWithoutExtension, resourceLoader).ifPresent(propertySources::add);
});
Micronaut.build(args)
.classes(Application.class)
.propertySources(propertySources.toArray(new PropertySource[1]))
.start();
} else {
Micronaut.run(Application.class, args);
}
}
}
As is, this code works for yaml config files(with snakeyaml in classpath). With minor changes, it can be made to work for properties files and to read config location from environment variable instead of system property. Full sample application present in github

Related

Logging from a groovlet

Is there a way to write into the server log from a groovlet script? I've tried the following to no avail:
Create a class with #Log4j annotation within the groovlet (#Log4j works fine in the rest of the project)
#Log4j('LOGGER')
class Log {
static Logger getLogger() {
LOGGER
}
}
Use ServerContext instance implicit variable application:
application.log('Hello world')
Any ideas welcome, thanks.
The logs didn't appear using the first method because of Log4j filtering on log4j.properties.
I created a separate class GroovletLogger inside one of our unfiltered packages and I can log fine from groovlets.

Setting release configuration for SPA / webpack

I'm developing a Single Page Application and using Webpack for bundling up the modules.
One of my source files (I'm using TypeScript) has settings and configuration used in the application e.g.
// app-settings.ts
export class AppSettings {
public static ApiUrlPrefix: string = "//localhost/myapi/";
}
Which I then use in my code like:
//some-class.ts
import {AppSettings} from "./app-settings"
export class SomeClass {
contructor() {
var something = AppSettings.ApiUrlPrefix;
}
}
When release time comes, I'm going to want the settings to match the live environment.
What's a good way with either gulp, npm or webpack configs to update the settings file? I've seen the HtmlWebpackPlugin which can take a template and plug in some options, so I guess I'm looking for something similar.
You can also use the webpack.DefinePlugin. The Define plugin allows you to pass "Free Global" or "macro-like" variables into your project that you can use. To use webpack.DefinePlugin simply require() webpack into your project.
Here's the documentation and an example on how to use it.

ClassLoader - Loading and saving data

Hopefully someone can help me with this.
It is my understanding that using a ClassLoader is the most reliable way to load in content.
public class Pipeline{
public static URL getResource(String filename) {
return ClassLoader.getSystemResource(filename);
}
public static InputStream getResourceAsStream(String filename) {
return ClassLoader.getSystemResourceAsStream(filename);
}
}
If you had a file at "[jar bundle]/resources/abc.png" ..You would load it by:
URL url = Pipeline.getResource("resources/abc.png");
Loading is simple.
Saving is what's getting me.
I have a program that collects data while running, saves that data on exit, and then loads the data back in next time and keeps adding to it.
Easiest solution I think would be to save back into the jar bundle so that ClassLoader can get at them. Is this even possible? Or recommended?
I don't mind having my resources outside of the jar, just as long as I don't have to resort to 'File' to get at them and save to them. (Unless it can be done cleanly)
folder/application.jar
folder/resources/abc.png
If you could ../ back one from where the ClassLoader is looking it would be easy to cleanly get data from the directory that actually contains the jar file
Pipeline.getResource("../resources/abc.png");
Any ideas?
This isn't really what class loaders are meant for. Loading resources from the class loader is meant so that you can bundle up your application as one package and components can read each other without worrying about how the system you're deploying to is setup.
If the file in the JAR is meant to be changed by the app, then it isn't part of the app and thus probably shouldn't be in the JAR.
I don't have a lot of context on your app, but hopefully my suggestion will be valid for your situation.
I recommend setting a requirement in your app that it has a work area to which it is allowed to read and write and accept a configuration setting that specifies where this directory is. Typical ways to do this in Java are with environment variables, system properties or JNDI settings (for container deployments).
Examples:
Tomcat's startup scripts figure out where it is installed and sets a system property called catalina.home and allows you to over-ride it with an environment variable called CATALINA_HOME.
JBoss looks for JBOSS_HOME
Java application servers typically look for JAVA_HOME to find the JDK.

Problems using an installclass in a web setup for a web site

I am trying to create a web setup for my web site, and I want to use an installer class to do some custom stuff. I am using VS 2010, and the web site and installer is .NET 3.5.
I have added reference to the installer class project output in the Install section under Custom Actions:
I have also set /targetdir="[TARGETDIR]/" on the CustomActionData for this action.
The InstallScript project is a standard class library (dll).
There is a public class that inherits from Installer class. It overrides the Install method as I have seen been done in several online examples:
using System.Collections;
using System.Windows.Forms;
namespace InstallScript
{
public class MyWebInstaller : System.Configuration.Install.Installer
{
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
var targetDir = Context.Parameters["targetdir"];
if(targetDir==null) targetDir = "No TARGETDIR!";
MessageBox.Show("TARGETDIR:\t" + targetDir);
}
}
}
I would think there should be shown a message box here som time during the install, but it seems like it is never called. No error is shown either. The setup just runs through as if this code was never called.
Anyone have idea of what is wrong?
OK, I found out what was missing.
You need to specify the class with the class attribute RunInstaller(true) for the setup to pick up and actually run the code.
So the class needs to be declared like this:
[System.ComponentModel.RunInstaller(true)]
public class MyWebInstaller : System.Configuration.Install.Installer
{
...

How to configure a default target in NLog

I am trying to convert existing logging library used by a bunch of apps to use NLog. The existing logging code has no indirection, logging calls go straight from the caller to a webservice call. The existing logging code is implemented as a static singleton. I need to do this in such a way that existing applications that use this library do not need configuration or code changes when they pick up the changed logging library. Later I can go update applications on an as needed basis, configuring a new logging target or changing the code to log to NLog directly.
To do this, I was going to make the static logging code go through NLog. Existing logging calls will be routed through NLog, and the existing webservice call will be wrapped in a custom NLog target.
To make this work on legacy apps without changing them, I need to programmatically set the custom target as the default (when none are configured in a config file). I'd like to do this without making config changes on the numerous existing applications, so I need to do this programmatically.
The problem is... its not working. Any thoughts on this approach? Below is the code I tried to add to hook in the existing logger class to create the default target.
I wouldn't mind going to log4net either. Just looking at the APIs I chose NLog initially as the names made more sense to me.
public static class LegacyLogger
{
static LegacyLogger()
{
if (LogManager.Configuration == null
|| LogManager.Configuration.GetConfiguredNamedTargets().Count == 0)
{
if (LogManager.Configuration == null)
{
LogManager.Configuration = new LoggingConfiguration();
}
//LogManager.Configuration.AddTarget("LegacyLogger", new NLog.Targets.DebuggerTarget());
LogManager.Configuration.AddTarget("LegacyLogger", new LegacyLoggerTarget());
}
_logger = LogManager.GetCurrentClassLogger();
}
Ok, if you're new to NLog, as I was, be advised the examples installed with the installer cover about everything. In this case, I needed:
public static class LegacyLogger
{
static LegacyLogger()
{
if (LogManager.Configuration == null
|| LogManager.Configuration.GetConfiguredNamedTargets().Count == 0)
{
NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(new LegacyLoggerTarget(), LogLevel.Trace);
}
_logger = LogManager.GetCurrentClassLogger();
}

Resources