Application Insights in Cuba Platform - java

I'm trying to use Azure Application Insight in Cuba Platform: https://learn.microsoft.com/en-us/azure/application-insights/app-insights-java-get-started
I managed to add the library to Cuba Platform, in particular to the Web module, but I couldn't set the "Instrumentation key" in any way.
There are three ways to do so:
1- Putting ApplicationInsights.xml in the "resource" folder
I couldn't find a place to put the file to be read by TelemetryConfigurationFactory class. Internally, I see it's using getResource() to scan in various "sensible" places.
I tried WEB-INF, META-INF, conf directory in Tomcat, conf/app, root of java package, work/app in Tomcat and probably something more with no results.
2- System property: -DAPPLICATION_INSIGHTS_IKEY=your_ikey
3- Environment variable: APPLICATION_INSIGHTS_IKEY
Tried both in a docker container, tried the last one locally: no results. In particular, System.getEnv is returning null even after exporting manually the variable locally, so this could be some error on my side
Any insight :D is welcome

I tried to search for the configuration of Instrumentation key from Application Insights source code.
I can see the snippet of code as below in the TelemetryConfigurationFactory Class.
/**
* Setting an instrumentation key:
* First we try the system property '-DAPPLICATION_INSIGHTS_IKEY=i_key' or '-DAPPINSIGHTS_INSTRUMENTATIONKEY=i_key'
* Next we will try the environment variable 'APPLICATION_INSIGHTS_IKEY' or 'APPINSIGHTS_INSTRUMENTATIONKEY'
* Next we will try to fetch the i-key from the ApplicationInsights.xml
* #param userConfiguration The configuration that was represents the user's configuration in ApplicationInsights.xml.
* #param configuration The configuration class.
*/
private void setInstrumentationKey(ApplicationInsightsXmlConfiguration userConfiguration, TelemetryConfiguration configuration) {
try {
String ikey;
// First, check whether an i-key was provided as a java system property i.e. '-DAPPLICATION_INSIGHTS_IKEY=i_key', or '-DAPPINSIGHTS_INSTRUMENTATIONKEY=i_key'
ikey = System.getProperty(EXTERNAL_PROPERTY_IKEY_NAME);
if (!Strings.isNullOrEmpty(ikey)) {
configuration.setInstrumentationKey(ikey);
return;
}
ikey = System.getProperty(EXTERNAL_PROPERTY_IKEY_NAME_SECONDARY);
if (!Strings.isNullOrEmpty(ikey)) {
configuration.setInstrumentationKey(ikey);
return;
}
// Second, try to find the i-key as an environment variable 'APPLICATION_INSIGHTS_IKEY' or 'APPINSIGHTS_INSTRUMENTATIONKEY'
ikey = System.getenv(EXTERNAL_PROPERTY_IKEY_NAME);
if (!Strings.isNullOrEmpty(ikey)) {
configuration.setInstrumentationKey(ikey);
return;
}
ikey = System.getenv(EXTERNAL_PROPERTY_IKEY_NAME_SECONDARY);
if (!Strings.isNullOrEmpty(ikey)) {
configuration.setInstrumentationKey(ikey);
return;
}
// Else, try to find the i-key in ApplicationInsights.xml
if (userConfiguration != null) {
ikey = userConfiguration.getInstrumentationKey();
if (ikey == null) {
return;
}
ikey = ikey.trim();
if (ikey.length() == 0) {
return;
}
configuration.setInstrumentationKey(ikey);
}
} catch (Exception e) {
InternalLogger.INSTANCE.error("Failed to set instrumentation key: '%s'", e.getMessage());
}
}
It seems that Instrumentation key can be configured as a java system property or as an environment variable or in ApplicationInsights.xml.
According to your description , System.getEnv("XXX") in your code return null.
You could check the possible reasons below :
Use My Computer > Advanced > Environment Variables to make a variable visible to all new processes.
2.The process which tries to read the variable is already running. Restart it.
3.The start script of Tomcat unsets the variable before it invokes java.exe
4.Tomcat unsets the variable in it's Java code.
In addition , you could refer to this thread : System.getenv() returns null when the environment variable exists.
Hope it helps you.

Related

java rmi simple project classNotFoundException binding registry

Ok, I'm sure this should be pretty easy, but I'm fairly new to Java (I'm more a .NET boy :P) and after following every single recommendation I found here to no success, I think it's time to step back and ask.
I'm trying to start a simple rmi project with a client, a server and a common project where common interfaces are defined. I've just implemented my server code, and when I try to run it to check if everything is fine, I get struck on a java.lang.ClassNotFoundException.
After following several answers on similar issues, I'm fair sure that my problem comes from rmiregistry running on a different location than my project.
I use following code to set registry codebase:
public class Utils {
public static final String CODEBASE = "java.rmi.server.codebase";
public static void setCodeBase(Class<?> c) {
String ruta = c.getProtectionDomain().getCodeSource().getLocation().toString();
String path = System.getProperty(CODEBASE);
if (path != null && !path.isEmpty()) {
ruta = path + " " + ruta;
}
System.setProperty(CODEBASE, ruta);
}
}
Then, I try to start my server code with this main class:
public class MainRegulador {
public static void main(String[] args) throws AccessException, RemoteException, NotBoundException {
Utils.setCodeBase(IRegulador.class);
Registry registro = null;
Remote proxy = null;
try {
Regulador myReg = new Regulador();
proxy = UnicastRemoteObject.exportObject(myReg, 36510);
registro = LocateRegistry.getRegistry();
registro.rebind("Distribuidor", proxy); //this is the line where exception is thrown
System.out.println("El Regulador está corriendo. Pulse ENTER para finalizar el proceso.");
System.in.read();
} catch(Exception ex) {
System.out.println("No se ha logrado inicializar el Registrador");
System.out.println(ex.getMessage());
} finally {
if (registro != null && proxy != null) {
registro.unbind("Distribuidor");
UnicastRemoteObject.unexportObject(proxy, true);
}
}
}
}
But when I run it, always get a java.lang.ClassNotFoundException at IRegulador interface.
Now the fun part:
I've printed to console java.rmi.server.codebase value, and it's pointing to bin folder of project where IRegulador interface is defined. (file:/F:/Practicas%20y%20dem%c3%a1s/Sistemas%20Distribuidos/common/bin/)
Obviously, that project is also set in the classpath of server project
(Regulador)
Workspace and rmiregistry are on different disks
Despite all, it doesn't seem a global classpath problem, as Utils class is on the same project as IRegulador interface, and it runs before the exception is thrown (as java.rmi.server.codebase is correctly set).
I've tried to set the classpath of rmiregistry before calling it (although it is directly discouraged on some answers), but nothing changed.
I've also tried to start rmiregistry.exe from Regulador project bin folder, but also seemed to don't change anything.
Coming from a .NET background, I've always found these classpath issues confusing, and this one is starting to consume much more time than I suspect it deserves. I'm in desperate need of help.
UPDATE: I'm starting to think that the problem is within the url it's passed to the codebase from IRegulador.class. If I paste it into windows explorer, the SO is unable to locate it, so I supose that it's being built with some structure problem that prevents the registry to reach the route:
file:/F:/Practicas%20y%20dem%c3%a1s/Sistemas%20Distribuidos/common/bin/
UPDATE2: I thought path route could be too complex, so I decided to simplify it and strip it from any non-straight character. Now codebase value is
file:/F:/Practicas/SD/common/bin/
However the problem persists, I don't know why rmiregistry is unable to reach that folder.
Then I decided to move the whole project to the same disk where rmiregistry is executed, and see if it changes anything. But nothing changed, same problem.
Ok, finally I got it working...
I've just copied rmiregistry.exe into the common/bin folder and launch it directly from there (previously just had called from there).
This seems to fix the problem with the routes (actually it makes the route available to the registry as it's on the same folder, probably all my codebase writting code is superflous now).

Is it possible to pass JVM arguments to a Jenkins 2.0 build?

I have a Java app that retrieves username/password creds from a resource property file.
For obvious reasons, I don't include the actual username/password values when I commit to SVN. I just make sure they're replaced with bogus values before committing.
Now I'm wanting to build my app on a remote Jenkins 2.0 box, however, since the values being stored in SVN are bogus, when Jenkins checks out my code and runs my integration tests, they fail because they can't authenticate when using the bogus values.
I was thinking maybe I could add some logic to my application that will first try to retrieve the username/password creds via a System.getProperty() and if those properties don't exist, then try looking at the resource file.
So, is it possible with Jenkins 2.0 to pass an argument to the JVM during a build so that a System.getProperty() would work?
You can do this using Jenkins Pipeline with declarative syntax:
Jenkinsfile:
pipeline {
agent {
label 'master'
}
environment {
//foo var
foo = "Hello !";
}
stages {
stage("test-var") {
steps {
// If you are using LINUX use EXPORT
bat "set foo=\"${env.foo}\""
bat "java -jar /someplace/DummyMain.jar"
}
}
}
}
Please notice I am using: System.getenv("foo"); instead of getProperty
Java Class:
/**
*
* #author daniel
*/
public class DummyMain {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String property = System.getenv("foo");
System.out.println(property);
}
}
You will get: Hello ! when you run your Pipeline. If you are more familiar with Script Groovy syntax instead of Pipeline Declarative Syntax should not be a problem.
Just write the logic for read the Property File and you are set !

ResourceBundle is unable to load properties file from package inside a webapp

I am working on refactoring a huge collection of code in an enterprise-scaled application.
The first step I want to take is to localize the log messages, so I decided to use the StringManager that I saw when I was reading the source code of Apache Tomcat.
The basic idea is to declare a file named LocalString.properties in the packages and define log messages in them. So in a way, this localizes the Strings to scope of that package only, so that it looks like:
Application
`----src
| `----main
| `----java
| `----package
| `----BusinessAction.java
| `----LocalStrings.properties
`----pom.xml
And an object of this StringManager is instantiated as:
private static final StringManager sm = StringManager.getManager("package");
This is constructor of the StringManager class.
/**
* Creates a new StringManager for a given package. This is a
* private method and all access to it is arbitrated by the
* static getManager method call so that only one StringManager
* per package will be created.
*
* #param packageName Name of package to create StringManager for.
*/
private StringManager(String packageName) {
String bundleName = packageName + ".LocalStrings";
try {
bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
} catch( MissingResourceException ex ) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if( cl != null ) {
try {
bundle = ResourceBundle.getBundle(
bundleName, Locale.getDefault(), cl);
} catch(MissingResourceException ex2) {
// Ignore
}
}
}
// Get the actual locale, which may be different from the requested one
if (bundle != null) {
locale = bundle.getLocale();
}
}
This works absolutely fine when used in a standalone application, but is causing issues when used in a web application.
I don't understand why the bundle fails to load on the first hand, but in the catch block, I can definitely tell that it is because the classloader is an instance of WebappClassloader.
What mistake I am making here? Also, what would be the good ways to load this property file?
Edit
The problem here is with the Maven build. Maven process all resources in src/main/resources and ignores the ones in src/main/java. So in the end, the compiled application did't had the LocalStrins.properties file where it was supposed to be.
Solved it by creating package in src/main/resources and putting the file in there.
The reason it fails is simply that the LocalStrings.properties file is not in your war file. In a Maven project, resources are supposed to be under src/main/resources. Not src/main/java. Maven will simply ignore resources under src/main/java, so they won't be in the built webapp at all, and thus won't be available at runtime.

Infinite scan for fonts in Apache FOP on CentOS

I use Apache Batik to convert SVG into PDF in one of the projects. The project is Spring application running in Tomcat 7. Everything works OK on development machine which runs under Ubuntu with Tomcat being started using $CATALINA_HOME/bin/startup.sh. But when I try to run the app on production server with CentOS 6 and Tomcat started using service tomcat7 start command the app falls into infinite loop on convertation. I've tried to debug the problem and found this piece of code:
/**
* Creates the {#link FontInfo} instance for the given configuration.
* #param cfg the configuration
* #param useComplexScriptFeatures true if complex script features enabled
* #return the font collection
* #throws FOPException if an error occurs while setting up the fonts
*/
public static FontInfo createFontInfo(Configuration cfg, boolean useComplexScriptFeatures)
throws FOPException {
FontInfo fontInfo = new FontInfo();
final boolean strict = false;
if (cfg != null) {
URI thisUri = new File(".").getAbsoluteFile().toURI();
InternalResourceResolver resourceResolver
= ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
//TODO The following could be optimized by retaining the FontManager somewhere
FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(),
FontCacheManagerFactory.createDefault());
//TODO Make use of fontBaseURL, font substitution and referencing configuration
//Requires a change to the expected configuration layout
DefaultFontConfig.DefaultFontConfigParser parser
= new DefaultFontConfig.DefaultFontConfigParser();
DefaultFontConfig fontInfoConfig = parser.parse(cfg, strict);
DefaultFontConfigurator fontInfoConfigurator
= new DefaultFontConfigurator(fontManager, null, strict);
List<EmbedFontInfo> fontInfoList = fontInfoConfigurator.configure(fontInfoConfig);
fontManager.saveCache();
FontSetup.setup(fontInfo, fontInfoList, resourceResolver, useComplexScriptFeatures);
} else {
FontSetup.setup(fontInfo, useComplexScriptFeatures);
}
return fontInfo;
}
in PDFDocumentGraphics2DConfigurator class. When I'm running the app on developer machine the line URI thisUri = new File(".").getAbsoluteFile().toURI(); results with thisUri being assigned with ~/tomcat/bin/. folder. When the app is running on production machine it is assigned with /. value. I think that this is the main problem because the value of thisUri is the folder in which FOP starts fonts search and on the production machine this is the root of file system and recursive search on the whole FS structure is very slow. I tried to add fop.xconf file to the WEB-INF directory with fonts configuration but it didn't affected the behavior of FOP. And I can't start Tomcat on production server the same way as I start on the dev machine.
Has anyone ideas on how to configure the base directory for font scan of FOR? Or am I doing something wrong?
I've found the workaround for the problem. I'm not sure if its wright or wrong to do stuff like that but it works. The workaround is based on the fact that File.getAbsolutFile() by default returns the directory resolved against the directory defined by user.dir option. So I need some way to pass this option on Tomcat service start. The only way I've found to do this is to add -Duser.dir=/%CATALINA_HOME/ to the CATALINA_OPTS variable defined in %CATALINA_HOME/bin/setenv.sh file. After that the font scan process took normal amount of time and my app started to work fine.

Load jar dynamically at runtime?

My current java project is using methods and variables from another project (same package). Right now the other project's jar has to be in the classpath to work correctly. My problem here is that the name of the jar can and will change because of increasing versions, and because you cannot use wildcards in the manifest classpath, it's impossible to add it to the classpath. So currently the only option of starting my application is using the -cp argument from the command line, manually adding the other jar my project depends on.
To improve this, I wanted to load the jar dynamically and read about using the ClassLoader. I read a lot of examples for it, however I still don't understand how to use it in my case.
What I want is it to load a jar file, lets say, myDependency-2.4.1-SNAPSHOT.jar, but it should be able to just search for a jar file starting with myDependency- because as I already said the version number can change at anytime. Then I should just be able to use it's methods and variables in my Code just like I do now (like ClassInMyDependency.exampleMethod()).
Can anyone help me with this, as I've been searching the web for a few hours now and still don't get how to use the ClassLoader to do what I just explained.
Many thanks in advance
(Applies to Java version 8 and earlier).
Indeed this is occasionally necessary. This is how I do this in production. It uses reflection to circumvent the encapsulation of addURL in the system class loader.
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws MyException
{
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new MyException(e);
}
}
I needed to load a jar file at runtime for both java 8 and java 9+. Here is the method to do it (using Spring Boot 1.5.2 if it may relate).
public static synchronized void loadLibrary(java.io.File jar) {
try {
java.net.URL url = jar.toURI().toURL();
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{url});
} catch (Exception ex) {
throw new RuntimeException("Cannot load library from jar file '" + jar.getAbsolutePath() + "'. Reason: " + ex.getMessage());
}
}

Categories