On Xampp Tomcat on Windows 11, I am trying to monitor java-web-app with java melody.
However, sql data is not detected by java melody.
Could you figure out what i am missing?
I have created a library project, not to do same settings on every app
Here is the projects code...
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tugalsan</groupId>
<artifactId>api-profile</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.bull.javamelody</groupId>
<artifactId>javamelody-core</artifactId>
<version>1.90.0</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api-url</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.gwt.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
TGS_ProfileServletUtils.java:
package com.tugalsan.api.profile.client;
import com.tugalsan.api.url.client.parser.*;
public class TGS_ProfileServletUtils {
final public static String SERVLET_NAME = "monitoring";//HARD-CODED IN LIB, THIS CANNOT BE CHANGED!
}
TS_ProfileMelodyUtils.java:
package com.tugalsan.api.profile.server.melody;
import java.sql.*;
import javax.sql.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import net.bull.javamelody.*;
import com.tugalsan.api.profile.client.*;
public class TS_ProfileMelodyUtils {
#WebFilter(
filterName = TGS_ProfileServletUtils.SERVLET_NAME,
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.ASYNC},
asyncSupported = true,
urlPatterns = {"/*"},
initParams = {
#WebInitParam(name = "async-supported", value = "true")
}
)
final public static class MelodyFilter extends MonitoringFilter {
}
#WebListener
final public static class MelodyListener extends SessionListener {
}
public static Connection createProxy(Connection con) {
try {
DriverManager.registerDriver(new net.bull.javamelody.JdbcDriver());
return JdbcWrapper.SINGLETON.createConnectionProxy(con);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static DataSource createProxy(DataSource ds) {
try {
DriverManager.registerDriver(new net.bull.javamelody.JdbcDriver());
return JdbcWrapper.SINGLETON.createDataSourceProxy(ds);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
a helper class
package com.tugalsan.api.sql.conn.server;
import java.io.Serializable;
import java.util.Objects;
public class TS_SQLConnConfig implements Serializable {
public int method = TS_SQLConnMethodUtils.METHOD_MYSQL();
public String dbName;
public String dbIp = "localhost";
public int dbPort = 3306;
public String dbUser = "root";
public String dbPassword = "";
public boolean autoReconnect = true;
public boolean useSSL = false;
public boolean region_ist = true;
public boolean charsetUTF8 = true;
public boolean isPooled = true;
public TS_SQLConnConfig() {//DTO
}
public TS_SQLConnConfig(CharSequence dbName) {
this.dbName = dbName == null ? null : dbName.toString();
}
}
On another api, this is how i create a pool
(I skipped some class files, unrelated to the question)
public static PoolProperties create(TS_SQLConnConfig config) {
var pool = new PoolProperties();
pool.setUrl(TS_SQLConnURLUtils.create(config));
pool.setDriverClassName(TS_SQLConnMethodUtils.getDriver(config));
if (TGS_StringUtils.isPresent(config.dbUser) && TGS_StringUtils.isPresent(config.dbPassword)) {
pool.setUsername(config.dbUser);
pool.setPassword(config.dbPassword);
}
var maxActive = 200;
pool.setMaxActive(maxActive);
pool.setInitialSize(maxActive / 10);
pool.setJmxEnabled(true);
pool.setTestWhileIdle(true);
pool.setTestOnBorrow(true);
pool.setTestOnReturn(false);
pool.setValidationQuery("SELECT 1");
pool.setValidationInterval(30000);
pool.setTimeBetweenEvictionRunsMillis(30000);
pool.setMaxWait(10000);
pool.setMinEvictableIdleTimeMillis(30000);
pool.setMinIdle(10);
pool.setFairQueue(true);
pool.setLogAbandoned(true);
pool.setRemoveAbandonedTimeout(600);
pool.setRemoveAbandoned(true);
pool.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;"
+ "org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer");
return pool;
}
WAY1:
//I created datasource once, save it as a global variable inside a ConcurrentLinkedQueue.
var pool_ds = new DataSource(create(config));
//then for every connection need, i created an extra proxy like this.
var pool_con = pool_ds.getConnection();
var proxy_con = TS_ProfileMelodyUtils.createProxy(pool_con);
//and close both of them later on
WAY1 RESULT:
WAY2:
//I created datasource once, save it as a global variable inside a ConcurrentLinkedQueue.
var pool_ds = new DataSource(create(config));
//then i created a proxy datasource, save it as a global variable too
var dsProxy = TS_ProfileMelodyUtils.createProxy(ds);
//then for every connection need, i did not create a proxy connection.
var pool_con = pool_ds.getConnection();
//and close connection later on
WAY2 RESULT: (same, nothing changed)
WAY3:
//I created datasource once, save it as a global variable inside a ConcurrentLinkedQueue.
var pool_ds = new DataSource(create(config));
//then i created a proxy datasource, save it as a global variable too
var dsProxy = TS_ProfileMelodyUtils.createProxy(ds);
//then for every connection need, i created an extra proxy like this.
var pool_con = pool_ds.getConnection();
var proxy_con = TS_ProfileMelodyUtils.createProxy(pool_con);
//and close both of them later on
WAY3 RESULT: (same, nothing changed)
I think i found the problem.
One should create connection from proxy_datasource not pool_datasource
var pool_con = pool_ds.getConnection(); //WRONG
var pool_con = proxy_ds.getConnection(); //RIGHT
And also, on creating statements,
one should use proxy connections (proxy_con) to create statements, not main connection (pool_con)!
I used way 3. And the results were singleton. I think java melody detects that it has a datasource already; and does not log twice.
full code: at github-profile
full code: at github-sql-conn
Related
I am learning to do unit and double tests with Junit and the Mockito framework, but I am not getting the expected result in a specific test with 'mocks'. I do an assertThat that should return positive test, instead, it returns an error that says Mockito cannot mock this class. It is a class called 'Console' that must print and collect values from the user's keyboard, but of course, in unit tests this should be 'mocked' to avoid 'intervening test' in antipattern, where the test asks for data to the developer, that is, I need to 'mock up' a user input. This 'Console' class is like a small facade of the typical java BufferedReader class.
I pass you the classes involved:
Console:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Console {
private BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
public String readString(String title) {
String input = null;
boolean ok = false;
do {
this.write(title);
try {
input = this.bufferedReader.readLine();
ok = true;
} catch (Exception ex) {
this.writeError("characte string");
}
} while (!ok);
return input;
}
public int readInt(String title) {
int input = 0;
boolean ok = false;
do {
try {
input = Integer.parseInt(this.readString(title));
ok = true;
} catch (Exception ex) {
this.writeError("integer");
}
} while (!ok);
return input;
}
public char readChar(String title) {
char charValue = ' ';
boolean ok = false;
do {
String input = this.readString(title);
if (input.length() != 1) {
this.writeError("character");
} else {
charValue = input.charAt(0);
ok = true;
}
} while (!ok);
return charValue;
}
public void writeln() {
System.out.println();
}
public void write(String string) {
System.out.print(string);
}
public void writeln(String string) {
System.out.println(string);
}
public void write(char character) {
System.out.print(character);
}
public void writeln(int integer) {
System.out.println(integer);
}
private void writeError(String format) {
System.out.println("FORMAT ERROR! " + "Enter a " + format + " formatted value.");
}
}
ConsoleTest:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.io.BufferedReader;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
public class ConsoleTest {
#Mock
private BufferedReader bufferedReader;
#InjectMocks
private Console console;
#BeforeEach
public void before(){
initMocks(this);
//this.console = new Console();
}
#Test
public void givenConsoleWhenReadStringThenValue() throws IOException {
String string = "yes";
when(this.bufferedReader.readLine()).thenReturn(string);
assertThat(this.console.readString("title"), is(string));
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
</modules>
<artifactId>solution.java.swing.socket.sql</artifactId>
<groupId>usantatecla.tictactoe</groupId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}.${project.artifactId}</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Thanks and greetings to the community!
I personally am not a huge fun of Mockito, I prefer to have full control of my classes using an interface and two implementations (one for production and one for test).
So this doesn't respond directly to your question about Mockito but it allows you to perfectly control the behaviour of your code without the need to use another framework.
You may define a very simple interface:
public interface Reader {
String readLine();
}
Then, you use that interface in your class Console:
public class Console {
private final Reader reader;
public Console(Reader reader) {
this.reader = reader;
}
//Replace all your this.bufferedReader.readLine() by this.reader.readLine();
}
So, in your production code you can use the real implementation with the Buffered reader:
public class ProductionReader implements Reader {
private final BufferedReader bufferedReader = new BufferedReader(...);
#Override
public String readLine() {
this.bufferedReader.readLine();
}
}
Console console = new Console(new ProductionReader());
... While in your tests you can use a test implementation:
public class TestReader implements Reader {
#Override
public String readLine() {
return "Yes";
}
}
Console console = new Console(new TestReader());
Note that while in your specific case you may mock the behaviour using Mockito, there are a lot of other cases when you will need a more complex approach and the a ove will allow you to have full control and full debuggability of your code without the need of adding any dependency.
I have already successfully deployed my app on Heroku, but my app is crashing while running.
I`m getting am error:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within
90 seconds of launch
I found in internet this code, which pasted in main class - no result:
public static String PORT = System.getenv("PORT");
public static String SERVER_URL = System.getenv("SERVER_URL");
Procfile:
web: java $JAVA_OPTS -Dserver.port=$PORT -cp
target/classes:target/dependency/* Bot
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>tgBot</artifactId>
<dependencies>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals><goal>copy-dependencies</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Main class:
import org.telegram.telegrambots.ApiContextInitializer;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
import java.io.IOException;
public class Bot extends TelegramLongPollingBot {
public static String PORT = System.getenv("PORT");
public static String SERVER_URL = System.getenv("SERVER_URL");
public static void main(String[] args) {
ApiContextInitializer.init();
TelegramBotsApi bot = new TelegramBotsApi();
try {
bot.registerBot(new Bot());
} catch (TelegramApiRequestException e) {
e.printStackTrace();
}
}
public void onUpdateReceived(Update update) {
Message message = update.getMessage();
Methods method = new Methods();
Answers answer = new Answers();
Model model = new Model();
if (message != null && message.hasText()) {
if (message.getText() == answer.row1Button) {
method.sendMsg(message, answer.faq);
}
String s = message.getText();
if ("/start".equals(s) || "Справка/помощь по боту".equals(s) || "/help".equals(s)) {
method.sendMsg(message, answer.faq);
} else if ("/api".equals(s)) {
method.sendMsg(message, answer.api);
} else {
try {
method.sendMsg(message, Weather.getWeather(message.getText(), model));
} catch (IOException e) {
method.sendMsg(message, answer.fail);
}
}
}
}
public String getBotUsername() {
return "Weather";
}
public String getBotToken() {
return "my bot token :D";
}
}
This could help you https://github.com/pengrad/telegram-bot-heroku, but it uses other library to work with Telegram Bot API – java-telegram-bot-api
There is a Procfile (need to update main class there) and build.gradle files for deploy.
By default it sets Webhook:
public class Main {
public static void main(String[] args) {
final String portNumber = System.getenv("PORT");
if (portNumber != null) {
port(Integer.parseInt(portNumber));
}
// current app url to set webhook
// should be set via heroku config vars
// https://devcenter.heroku.com/articles/config-vars
// heroku config:set APP_URL=https://app-for-my-bot.herokuapp.com
final String appUrl = System.getenv("APP_URL");
// define list of bots
BotHandler[] bots = new BotHandler[]{
new TestTelegramBot()
};
// set bot to listen https://my-app.heroku.com/BOTTOKEN
// register this URL as Telegram Webhook
for (BotHandler bot : bots) {
String token = bot.getToken();
post("/" + token, bot);
if (appUrl != null) {
bot.getBot().execute(new SetWebhook().url(appUrl + "/" + token));
}
}
}
}
Can easily change to long polling:
bot.setUpdatesListener(updates -> {
for (Update update : updates) {
onUpdateReceived(update);
}
return UpdatesListener.CONFIRMED_UPDATES_ALL;
});
I've installed the mongo driver in my running Karaf server:
bundle:install -s wrap:mvn:org.mongodb/mongo-java-driver/3.6.3
I'm simply trying to connect to the DB and log the databases I have. Currently running out of the box local instance. Below is the code I wrote to demo this in OSGI/Karaf. I'm using the mvn bundle plugin.
I created a database under the alias osgiDatabase
I'm running my debugger and the failure happens during the instantiation of the MongoClient() but not understanding what I could be doing wrong.
This works when I don't use Karaf. The only error I get is Activator start error in bundle
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qa</groupId>
<artifactId>board</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>com.mongodb, org.osgi.framework</Import-Package>
<Bundle-Activator>Connection.Activator</Bundle-Activator>
<Export-Package>*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
DBUtil
package Connection;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import java.util.List;
public class DBUtil {
MongoClient client;
MongoDatabase database;
public DBUtil() {
}
public DBUtil(String databaseName) {
if (client == null) {
client = new MongoClient();
database = client.getDatabase(databaseName);
}
}
/**
* Allows you to reveal all databases under the current connection
*/
public void showDatabases() {
if (client == null) {
throw new NullPointerException();
}
List<String> databases = client.getDatabaseNames();
for (String db : databases) {
System.out.println("The name of the database is: " + db);
}
}
}
Activator
package Connection;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
public void start(BundleContext bundleContext) throws Exception {
DBUtil util = new DBUtil("osgiDatabase");
// util.showDatabases();
System.out.println("Working");
}
public void stop(BundleContext bundleContext) throws Exception {
System.out.println("Bundle disabled");
}
}
Your Import-Package configuration looks wrong. If you configure it explicitly like this you switch off the auto detection of needed packages. So it is very likely you are missing some packages your code needs.
Instead try to only configure the activator and leave the rest on defaults.
To get better logs you should use a try catch in your Activator an log the exception using slf4j. So you get some more information what is wrong.
I'm trying to add some functional tests on existing netbeans application.
Info about application: packaged by maven, used netbeans platform 7.3.1.
I've added dependencies how described in this article but got exception:
Running qa.FuncTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.067 sec <<< FAILURE! - in qa.FuncTest
org.netbeans.junit.NbModuleSuite$S#67ad77a7(org.netbeans.junit.NbModuleSuite$S) Time elapsed: 0.066 sec <<< ERROR!
java.lang.ClassNotFoundException: org.netbeans.Main
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at org.netbeans.junit.NbModuleSuite$S.runInRuntimeContainer(NbModuleSuite.java:819)
at org.netbeans.junit.NbModuleSuite$S.access$100(NbModuleSuite.java:667)
Does anybody know why it happend? And how to fix it?
Thanks in advance.
UPD dependency section from application/pom.xml
<dependencies>
<dependency>
<groupId>org.netbeans.cluster</groupId>
<artifactId>platform</artifactId>
<version>${software.netbeans.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-jdesktop-beansbinding</artifactId>
<version>${software.netbeans.version}</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-nbjunit</artifactId>
<version>${software.netbeans.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-jellytools-platform</artifactId>
<version>${software.netbeans.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
UPD1 test class:
package qa;
import junit.framework.Test;
import org.netbeans.jellytools.JellyTestCase;
import org.netbeans.jellytools.OptionsOperator;
import org.netbeans.junit.NbModuleSuite;
import org.openide.windows.TopComponent;
public class FuncTest extends JellyTestCase {
public static Test suite() {
return NbModuleSuite.allModules(FuncTest.class);
}
public FuncTest(String n) {
super(n);
}
public void testWhatever() throws Exception {
TopComponent tc = new TopComponent();
tc.setName("label");
tc.open();
OptionsOperator.invoke().selectMiscellaneous();
Thread.sleep(5000);
System.err.println("OK.");
}
}
I would like to share results of my investigation. I noticed when application was started as usual i saw in output window:
Installation =.../application/target/application/extra
.../application/target/application/java
.../application/target/application/kws
.../application/target/application/platform
but when application was started via nbjubit/jellytool i saw only:
Installation =.../application/target/application/platform
so i decided to expand this values and investigated source code. Let's consider a few interesting methods in NbModuleSuite.java :
private static String[] tokenizePath(String path) {
List<String> l = new ArrayList<String>();
StringTokenizer tok = new StringTokenizer(path, ":;", true); // NOI18N
.....
}
static File findPlatform() {
String clusterPath = System.getProperty("cluster.path.final"); // NOI18N
if (clusterPath != null) {
for (String piece : tokenizePath(clusterPath)) {
File d = new File(piece);
if (d.getName().matches("platform\\d*")) {
return d;
}
}
}
String allClusters = System.getProperty("all.clusters"); // #194794
if (allClusters != null) {
File d = new File(allClusters, "platform"); // do not bother with old numbered variants
if (d.isDirectory()) {
return d;
}
}
....
}
static void findClusters(Collection<File> clusters, List<String> regExps) throws IOException {
File plat = findPlatform().getCanonicalFile();
String selectiveClusters = System.getProperty("cluster.path.final"); // NOI18N
Set<File> path;
if (selectiveClusters != null) {
path = new TreeSet<File>();
for (String p : tokenizePath(selectiveClusters)) {
File f = new File(p);
path.add(f.getCanonicalFile());
}
} else {
File parent;
String allClusters = System.getProperty("all.clusters"); // #194794
if (allClusters != null) {
parent = new File(allClusters);
} else {
parent = plat.getParentFile();
}
path = new TreeSet<File>(Arrays.asList(parent.listFiles()));
}
....
}
As you can see we can set path values in cluster.path.final or all.clusters and use ; : as delimeters. I spent some time to play with this constants and realised that settings didn't set up in pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${software.maven-surefire-plugin}</version>
<configuration>
<skipTests>false</skipTests>
<systemPropertyVariables>
<branding.token>${brandingToken}</branding.token>
<!--problem part start-->
<property>
<name>cluster.path.final</name>
<value>${project.build.directory}/${brandingToken}/platform:${project.build.directory}/${brandingToken}/java:...etc</value>
</property>
<!--problem part end-->
</systemPropertyVariables>
</configuration>
</plugin>
but this work well:
<properties>
<cluster.path.final>${project.build.directory}/${brandingToken}/platform:${project.build.directory}/${brandingToken}/java:...etc</cluster.path.final>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${software.maven-surefire-plugin}</version>
<configuration>
<skipTests>false</skipTests>
<systemPropertyVariables>
<branding.token>${brandingToken}</branding.token>
<cluster.path.final>${cluster.path.final}</cluster.path.final>
</systemPropertyVariables>
</configuration>
</plugin>
I don't know why it happens but I would recommend to use maven section properties to set systemPropertyVariables of maven-surefire-plugin. Good luck!
I have a maven project with several modules, i.e.
<module>backend</module> <!-- provides annotations -->
<module>annotationProcessor</module> <!-- processes ann., generates files -->
<module>mainprog</module> <!-- uses annotations/files -->
backend provides an annotation class MyAnnotation for annotating classes.
mainprog contains Mainprog.java which defines a class with a #MyAnnotation annotation. At runtime this class tries to load a file via getResourceAsStream("Mainprog.properties") (which does not exist yet).
The annotationProcessor has a class MyAnnotationProcessor which maven executes and finds my annotations.
The processor should create the file Mainprog.properties from information gathered by the annotation processor.
I can not manage to put the properties file in a place where it is found when executing/testing Mainprog.
Where should I generate the file to into, being in a maven workflow?
How do I tell maven this file is used in tests or at runtime? Eventually
is has to be packaged in the jar.
Mainprog
package demo;
#MyAnnotation
public class Mainprog {
}
Use the properties file
Currently I do it in the testing class, but later this will be in the class itself.
package demo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.junit.Test;
public class MainprogTest {
Class testclass = Mainprog.class;
#Test
public void testPropertiesFile() throws IOException {
String fn = testclass.getCanonicalName().replace('.', '/') + ".properties";
System.err.println("loading: '"+fn+"'");
InputStream in = getClass().getResourceAsStream(fn);
Properties prop = new Properties();
prop.load(in);
in.close();
}
}
This currently runs as such:
loading: 'demo/Mainprog.properties'
Tests in error:
testPropertiesFile(demo.MainprogTest)
with a NullPointerException, because the stream returns null, i.e. does not exist.
Despite the file is there (but is it in the right place?):
towi#havaloc:~/git/project/mainprog$ find . -name Mainprog.properties
./src/java/demo/Mainprog.properties
./target/classes/demo/Mainprog.properties
Processor
package demo;
import com.github.javaparser.*;
import com.github.javaparser.ast.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
#SupportedAnnotationTypes({"demo.MyAnnotation"})
public class MyAnnotationProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
for (TypeElement te : elements) {
for (Element e : env.getElementsAnnotatedWith(te))
{
processAnnotation(e);
}
}
return true;
}
private void processAnnotation(Element elem) {
final TypeElement classElem = (TypeElement) elem;
...
final String prefix = System.getProperty("user.dir").endsWith("/"+"mainprog") ? "." : "mainprog";
final String className = classElem.getQualifiedName().toString();
String fileName = prefix + "/src/java/" + className.replace('.', '/') + ".java";
FileInputStream in = new FileInputStream(fileName);
final CompilationUnit cu = JavaParser.parse(in);
final CallGraph graph = ...
generateInfoProperties(classElem, fileName, graph);
}
private void generateInfoProperties(TypeElement classElem, String inFilename, CallGraph graph) throws IOException {
final File outFile = new File(inFilename
.replace("/src/java/", "/src/java/") // <<< WHERE TO ???
.replace(".java", ".properties"));
outFile.getParentFile().mkdirs();
try (PrintWriter writer = new PrintWriter(outFile, "UTF-8")) {
final Properties ps = new Properties();
graph.storeAsProperties(ps);
ps.store(writer, inFilename);
writer.close();
}
}
}
As you can see, there is a lot of guesswork and "heuristics" going on when handling directory names. All that System.getProperty("user.dir") and replace("/src/java/", "/src/java/") is probably wrong, but what is better?
maven
In Maven I have 4 poms, of course
pom.xml
backend/pom.xml
annotationProcessor/pom.xml
mainprog/pom.xml
Only one of seems to me contains anything of note, i.e., the execution of the annotation processor in mainprog/pom.xml:
<project>
....
<dependencies>
<dependency>
<groupId>project</groupId>
<artifactId>backend</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>project</groupId>
<artifactId>annotationProcessor</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>mainprog</finalName>
<sourceDirectory>src/java</sourceDirectory>
<resources>
<resource>
<directory>${basedir}/src/conf</directory>
<targetPath>META-INF</targetPath>
</resource>
<resource>
<directory>${basedir}/web</directory>
</resource>
<resource>
<directory>${basedir}/src/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.wsdl</include>
<include>**/*.xsd</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessors>
<annotationProcessor>demo.MyAnnotationProcessor
</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
I thought by generating the file into /src/java/ and then having <resource><directory>${basedir}/src/java and <include>**/*.properties is enough, but it does not seem so. Why is that?
Use the provided Filer, which can be obtained using processingEnv.getFiler(). If you create a source file using it, the compiler will compile it on the next round and you won't need to worry about configuring Maven to compile generated source files.