I want to add some log.debug statements to a class I'm working on, and I'd like to see that in output when running the test. I'd like to override the log4j properties on the command line, with something like this:
-Dlog4j.logger.com.mypackage.Thingie=DEBUG
I do this kind of thing frequently. I am specifically only interested in a way to pass this on the command line. I know how to do it with a config file, and that doesn't suit my workflow.
As part of your jvm arguments you can set -Dlog4j.configuration=file:"<FILE_PATH>". Where FILE_PATH is the path of your log4j.properties file.
Please note that as of log4j2, the new system variable to use is log4j.configurationFile and you put in the actual path to the file (i.e. without the file: prefix) and it will automatically load the factory based on the extension of the configuration file:
-Dlog4j.configurationFile=/path/to/log4jconfig.{ext}
These answers actually dissuaded me from trying the simplest possible thing! Simply specify a threshold for an appender (say, "console") in your log4j.configuration like so:
log4j.appender.console.threshold=${my.logging.threshold}
Then, on the command line, include the system property -Dlog4j.info -Dmy.logging.threshold=INFO. I assume that any other property can be parameterized in this way, but this is the easiest way to raise or lower the logging level globally.
With Log4j2, this can be achieved using the following utility method added to your code.
private static void setLogLevel() {
if (Boolean.getBoolean("log4j.debug")) {
Configurator.setLevel(System.getProperty("log4j.logger"), Level.DEBUG);
}
}
You need these imports
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
Now invoke the setLogLevel method in your main() or whereever appropriate and pass command line params -Dlog4j.logger=com.mypackage.Thingie and -Dlog4j.debug=true.
log4j does not support this directly.
As you do not want a configuration file, you most likely use programmatic configuration. I would suggest that you look into scanning all the system properties, and explicitly program what you want based on this.
Based on Thorbjørn Ravn Andersens suggestion I wrote some code that makes this work
Add the following early in the main method and it is now possible to set the log level from the comand line. This have been tested in a project of mine but I'm new to log4j and might have made some mistake. If so please correct me.
Logger.getRootLogger().setLevel(Level.WARN);
HashMap<String,Level> logLevels=new HashMap<String,Level>();
logLevels.put("ALL",Level.ALL);
logLevels.put("TRACE",Level.TRACE);
logLevels.put("DEBUG",Level.DEBUG);
logLevels.put("INFO",Level.INFO);
logLevels.put("WARN",Level.WARN);
logLevels.put("ERROR",Level.ERROR);
logLevels.put("FATAL",Level.FATAL);
logLevels.put("OFF",Level.OFF);
for(String name:System.getProperties().stringPropertyNames()){
String logger="log4j.logger.";
if(name.startsWith(logger)){
String loggerName=name.substring(logger.length());
String loggerValue=System.getProperty(name);
if(logLevels.containsKey(loggerValue))
Logger.getLogger(loggerName).setLevel(logLevels.get(loggerValue));
else
Logger.getRootLogger().warn("unknown log4j logg level on comand line: "+loggerValue);
}
}
In my pretty standard setup I've been seeing the following work well when passed in as VM Option (commandline before class in Java, or VM Option in an IDE):
-Droot.log.level=TRACE
Based on #lijat, here is a simplified implementation. In my spring-based application I simply load this as a bean.
public static void configureLog4jFromSystemProperties()
{
final String LOGGER_PREFIX = "log4j.logger.";
for(String propertyName : System.getProperties().stringPropertyNames())
{
if (propertyName.startsWith(LOGGER_PREFIX)) {
String loggerName = propertyName.substring(LOGGER_PREFIX.length());
String levelName = System.getProperty(propertyName, "");
Level level = Level.toLevel(levelName); // defaults to DEBUG
if (!"".equals(levelName) && !levelName.toUpperCase().equals(level.toString())) {
logger.error("Skipping unrecognized log4j log level " + levelName + ": -D" + propertyName + "=" + levelName);
continue;
}
logger.info("Setting " + loggerName + " => " + level.toString());
Logger.getLogger(loggerName).setLevel(level);
}
}
}
Related
With the same code, I want to run it both as a real mode and a dev mode.
So I added a service-mode argument, and it decides the port, database name, etc.
I also want to make a log file for a dev mode.
But using log4j, it seems that configuration about logging is decided when app is started.
Is there a way to change the log file path depending on a given argument?
I expected something like:
// Main.java
if (args.serviceMode is real) {
setLogging(log4j.properties)
}
else {
setLogging(log4j.properties.dev)
}
or
// log4j.properties
log4j.appender.file.File=/path/to/real/log
log4jdev.appender.file.File=/path/to/dev/log
// Main.java
if (args.serviceMode is real) {
setLogPath(log4j.appender.file.File)
}
else {
setLogPath(log4jdev.appender.file.File)
}
Any link or comment appreciated.
You can add spring.profiles in your application.yml and set new log-name
spring.profiles: dev
logging.file: new-app.log
More information you can find get here
Is it possible to take cucumber option values from a java .properties file?
In this SO post, it shows that it is being passed from CLI.
Here's my sample class:
#RunWith(Cucumber.class)
#CucumberOptions(
features = {"resources/features/"},
glue = {"classpath:com/"},
tags = {"#foo, #bar"}
)
public class UITestRunner {
}
Instead of hardcoding the tags here, I'd like to take it from a property file.
Any help is appreciated!
Cucumber will initially look for arguments provided by cucumber.api.cli.Main or #CucumberOptions
But you can override them providing (in this particular order):
The OS environment variable CUCUMBER_OPTIONS
The Java system property cucumber.options
The Java resource bundle cucumber.properties with a cucumber.options property
Once one of described above options is found, it will be used. Overrides are provided in a variable (or property) called cucumber.options or CUCUMBER_OPTIONS. All values, except plugin arguments will override values provided by cucumber.api.cli.Main or #CucumberOptions. Plugin option will add up to the plugins specified by cucumber.api.cli.Main or #CucumberOptions.
Hope you are aware that if running from the command line, you can use system properties
mvn test -Dcucumber.options="--features resources/features/ --tags ~#ignore" -Dtest=AnimalsTest
Which means that you can programmatically set these properties:
#RunWith(Cucumber.class)
public class CatsRunner {
#BeforeClass
public static void before() {
System.setProperty("cucumber.options", "--features resources/features/ --tags ~#ignore");
}
}
Hope that gives you some ideas. For example, you can manually read the properties from a file and then achieve what you want.
Edit: apparently the above does not work. So here's my next idea, implement your own JUnit Cucumber runner by extending the Cucumber class. Refer to this for an example. So in the constructor you should have full control.
I solved this by extending the Cucumber runner. You can find examples here:
For cucumber-jvm 4.0.0: https://github.com/martinschneider/yasew/blob/master/src/main/java/io/github/martinschneider/yasew/junit/YasewRunner.java
For cucumber-jvm 2.4.0: https://github.com/martinschneider/yasew/blob/db8cd74281139c14603e9ae05548530a7aebbade/src/main/java/io/github/martinschneider/yasew/junit/YasewRunner.java
The key part, as discussed in some of the replies and comments, is to set the cucumber.options system property:
String cucumberOptions =
"--tags #"
+ getProperty(PLATFORM_KEY, DEFAULT_PLATFORM)
+ " --glue io.github.martinschneider.yasew.steps --glue "
+ getProperty(STEPS_PACKAGE_KEY)
+ " --plugin pretty --plugin html:report --plugin json:"
+ getProperty(CUCUMBER_REPORT_DIRECTORY_KEY,
DEFAULT_CUCUMBER_REPORT_DIRECTORY)
+ "/cucumber.json"
+ " "
+ getProperty(FEATURES_DIRECTORY_KEY);
LOG.info("Setting cucumber options ({}) to {}", CUCUMBER_OPTIONS_KEY, cucumberOptions);
System.setProperty(CUCUMBER_OPTIONS_KEY, cucumberOptions);
I'm using a setup with Spring and JUnit and I'm not sure if there's a better place to put this code.
Overwriting the runner is not very elegant but it works like a charm!
An example for an override feature source line in cucumber.properties file in project tree is:
cucumber.options=-g StepDefs src\\test\\resources\\Testfeature.feature
The Cucumber for Java Book is cool. I got it after reading this post.
I experimented some time to see what path the CucumberOptions property accepts... so here above is the quick solve. ;)
StepDefs is the folder where my step definitions are located in the project tree.
I prefer this way to have everything in one place. Maybe for porting the testsuite to another system it is more common to set a System variable in the target system so the possible customer has always one directory where to place feature-files.
I was searching for a solution how to pass(over write) feature file path glue (steps) path in command line in Dcucumber options. It was quite challenging and I was unable to find the exact solution in many of the forums. Finally found a working solution
Just posting here it could help anybody.
gradle -Dcucumber.options="-g XX.XXX.XXX.steps --tags #xxxxxx featurefilepath/features/" test
You must follow this order having -g as a first option. Thaanks
I am doing like this:-
cucmberOption.properties
#cucumber.options=--plugin html:output/cucumber-html-report
#src/test/resources
cucumber.options.feature =src/test/resources
cucumber.options.report.html=--plugin html:output/cucumber-html-report
Java Class: CreateCucumberOptions.java
Method to load properties file:-
private static void loadPropertiesFile(){
InputStream input = null;
try{
String filename = "cucumberOptions.properties";
input = CreateCucumberOptions.class.getClassLoader().getResourceAsStream(filename);
if(input==null){
LOGGER.error("Sorry, unable to find " + filename);
return;
}
prop.load(input);
}catch(IOException e){
e.printStackTrace();
}finally{
if(input!=null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
method to get and set CucumberOptions
private String createAndGetCucumberOption(){
StringBuilder sb = new StringBuilder();
String featureFilesPath =
prop.getProperty("cucumber.options.feature");
LOGGER.info(" featureFilesPath: " +featureFilesPath);
String htmlOutputReport =
prop.getProperty("cucumber.options.report.html");
LOGGER.info(" htmlOutputReport: " +htmlOutputReport);
sb.append(htmlOutputReport);
sb.append(" ");
sb.append(featureFilesPath);
return sb.toString();
}
private void setOptions(){
String value = createAndGetCucumberOption();
LOGGER.info(" Value: " +value);
System.setProperty(KEY, value);
}
And main method to run this:-
public static void main(String[] args) {
CreateCucumberOptions cucumberOptions = new CreateCucumberOptions();
JUnitCore junitRunner = new JUnitCore();
loadPropertiesFile();
cucumberOptions.setOptions();
junitRunner.run(cucumberTest.runners.RunGwMLCompareTests.class);
}
And RunGwMLCompareTests.class is my Cucumber class
#RunWith(Cucumber.class)
#CucumberOptions(
monochrome = true,
tags = {"#passed"},
glue = "cucumberTest.steps")
public class RunGwMLCompareTests {
public RunGwMLCompareTests(){
}
}
So basically nopw you get set output report and feature folders through properties files and others options like glue definations java class. And to run the test cases just run your main class.
Regards,
Vikram Pathania
I am not sure if that is possible but I have a old java application projects which have 1000+ java files. I am trying to add log4j support to the application which require me to add
public static Logger logger = Logger.getLogger(MyClass.class.getName());
in every file.
Is there any way I can perform the operation using eclipse. I have tried source->format but that is not allowing me to add the line. Do I have to open every file and add that line?
You could make use of templates in eclipse, but in this case, you need to edit each file and add it.
Update :
Save following content in some file-named with extension ".xml"
<?xml version="1.0" encoding="UTF-8" standalone="no"?><templates><template autoinsert="true" context="java-members" deleted="false" description="adds the logger statement" enabled="true" name="logger">public static Logger logger = Logger.getLogger(${enclosing_type}.class.getName());</template></templates>
Press CTRL+3, Type - "templates" and choose for Templates- Java Editor as shown below
Import the file from menu from right as shown below
now go to any of your file and type "logger"in your class file and do CTRL+space , quick assist will show you the "logger" template
as shown below
and your logging statement will appear, with your class in which you are editing as shown below
You can do it programatically. Start with a filter for all your .java:
public class FileExtensionFilter implements FilenameFilter {
private Set<String> filteredExtensions;
public FileExtensionFilter() {
filteredExtensions = new HashSet<String>();
}
#Override
public boolean accept(File dir, String name) {
boolean accept = true;
for (String filteredExtension:filteredExtensions) {
accept = accept && !name.endsWith(filteredExtension);
}
return accept;
}
public void addFilteredExtension(String extension) {
filteredExtensions.add(extension);
}
}
Then you can look for the file using a recursive method:
public Set<String> searchFileBasedOnExtension(File file) {
Set<String> extensions = new HashSet<String>();
if (file.isDirectory()) {
for (File f : file.listFiles(fileExtensionFilter)) {
extensions.addAll(checkForExtensions(f));
}
} else {
String extension = file.getName().substring(Math.max(file.getName().lastIndexOf('.'),0));
extensions.add(extension);
fileExtensionFilter.addFilteredExtension(extension);
}
return extensions;
}
Then based on the set you receive, you can iterate it, read the file to find the position to add the "import" and also find the class name, and save it into a variable to replace it for each file, since each file represents a different class.
Sample:
for (String s : setWithFileNames) {
// Use BufferedReader to read the file, save the content in a String, then look inside the String the classname and the first import position.
// Use bufferedWriter to re-write the file with the changes you made.
}
Hope it gives you a hand with your requirement. Best regards.
Opening each file and adding this line would be tedious.
I am not sure if Eclipse has such thing.
But I would suggest to go for a shell script or a Java function to do this.
Read each file.
Search for the first '{' character.
Insert the logger statement in the line next to that.
You can get the class name from the file name.
I know this might not be the best solution for you.
Hope this helps.
If you have notepad ++ you can do it. Use the find replace feature for a direcory using regular expressions.
so a line starting with public class need to be replaced by the line and another line with the log statement.
Another suggestion is to use AspectJ. AspectJ is an extension to Java that allows you to systematically weave in extra functionality to existing classes. You would first need to install AJDT (AspectJ development tools). Then you need to create an Aspect like this:
aspect LoggingAspect {
before (Object thiz) : execution(public * *(..)) && this(thiz) {
Logger logger = Logger.getLogger(thiz.getClass().getName());
logger.log("Something");
}
}
The aspect above will log every execution of a public method of one of the classes that you compile. You can certainly tweak this in many ways. Logging is one of the simplest things that AspectJ can do. The nice thing about going down this path is that you can easily enable/disable logging from your project by commenting out 4 lines.
The main AspectJ website. And the AspectJ programming guide is a good place to start with AspectJ.
You can run the below command to add any line on the specific line number, Just provide the folder name it will search all the subfolders with the file extension as .java and add the line on the specific position:
find <Folder Path> -name "*.java" -exec sed -i '1i<Text: You want to add>' {} \;
api 1.7 and slf4j-simple as implementation. I just can't find how to configure the logging level with this combination.
Can anyone help out?
It's either through system property
-Dorg.slf4j.simpleLogger.defaultLogLevel=debug
or simplelogger.properties file on the classpath
see https://www.slf4j.org/api/org/slf4j/simple/SimpleLogger.html for details
This is a sample simplelogger.properties which you can place on the classpath (uncomment the properties you wish to use):
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
#org.slf4j.simpleLogger.defaultLogLevel=info
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
#org.slf4j.simpleLogger.showDateTime=false
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
#org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false
In a Maven or Gradle project, a convenient place "on the classpath" is src/main/resources/simplelogger.properties.
You can programatically change it by setting the system property:
public class App {
public static void main(String[] args) {
// for the code below to work, it must be executed before the
// logger is created. see note below
System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE");
org.slf4j.Logger log = LoggerFactory.getLogger(App.class);
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warning");
log.error("error");
}
}
The log levels are ERROR > WARN > INFO > DEBUG > TRACE.
Please note that once the logger is created the log level can't be changed. If you need to dynamically change the logging level you might want to use log4j with SLF4J.
I noticed that Eemuli said that you can't change the log level after they are created - and while that might be the design, it isn't entirely true.
I ran into a situation where I was using a library that logged to slf4j - and I was using the library while writing a maven mojo plugin.
Maven uses a (hacked) version of the slf4j SimpleLogger, and I was unable to get my plugin code to reroute its logging to something like log4j, which I could control.
And I can't change the maven logging config.
So, to quiet down some noisy info messages, I found I could use reflection like this, to futz with the SimpleLogger at runtime.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
try
{
Logger l = LoggerFactory.getLogger("full.classname.of.noisy.logger"); //This is actually a MavenSimpleLogger, but due to various classloader issues, can't work with the directly.
Field f = l.getClass().getSuperclass().getDeclaredField("currentLogLevel");
f.setAccessible(true);
f.set(l, LocationAwareLogger.WARN_INT);
}
catch (Exception e)
{
getLog().warn("Failed to reset the log level of " + loggerName + ", it will continue being noisy.", e);
}
Of course, note, this isn't a very stable / reliable solution... as it will break the next time the maven folks change their logger.
I don't know why. I use simplelogger.properties and org.slf4j.simpleLogger.showDatetime, it's not working.
I lookup the SimpleLogger class source code and got this part of the code
static {
// Add props from the resource simplelogger.properties
InputStream in = (InputStream)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
if (threadCL != null) {
return threadCL.getResourceAsStream(CONFIGURATION_FILE);
} else {
return ClassLoader.getSystemResourceAsStream(CONFIGURATION_FILE);
}
}
});
if(null != in) {
try {
simpleLoggerProps.load(in);
in.close();
} catch(java.io.IOException e) {
// ignored
}
}
showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
showThreadName = getBooleanProperty(systemPrefix + "showthreadname", showThreadName);
dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", dateTimeFormat);
if(showDateTime) {
try {
dateFormatter = new SimpleDateFormat(dateTimeFormat);
} catch(IllegalArgumentException e) {
Util.report("Bad date format in " + CONFIGURATION_FILE + "; reverting to default", e);
// If the format pattern is invalid - use the default format
dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
dateFormatter = new SimpleDateFormat(dateTimeFormat);
}
}
}
systemPrefix + "showdatetime" is org.slf4j.simplelogger.showdatetime
When I try write org.slf4j.simplelogger.showdatetime=true to simplelogger.properties, It works normally. I hope it can help some people.
I have a java class file with a main method. In Windows, I would like to be able to drag files onto a desktop icon/short/etc that would call supply the filenames to my main method. Basically, I want to allow users to drag-and-drop files at program execution instead of having type them on the command line.
Any thoughts?
To build on daub815's answer, in Windows, you can use a batch file to pass
arguments to another command. In this case, we'll use the java launcher to
launch your class with the main method.
I did a quick Google search on how to do write a batch file to take multiple arguments,
and found a page with a batch file to pass arguments to another command. Adapting from
the example, here is what you can do:
#ECHO OFF
:Loop
IF "%1" == "" GOTO Done
java YourClass %1
SHIFT
GOTO Loop
:Done
Save the above file as a batch file (with a ".bat" extension), and then you can drag-and-drop
files onto it, and it will be passed as arguments.
Also, you can call the batch file from the command line and pass arguments as well.
Edit: It appears that the batch file will not work with quoted arguments which contain spaces. Using a workaround presented in the site I've linked to will split the spaces contained in the quoted full path of the file into separate arguments, so that won't work either. If anyone has a good idea how to fix this, please either edit this entry, or post another answer. I will make this a community wiki.
PhiLho's answer works perfectly if you pack the classes in an executable JAR file (it's how you're meant to do it anyway) and make a .reg file that looks like the one below. Then just double-click that .reg file to merge it into the registry and you're good to go. This lets you both double-click a JAR file to run it, and starting it by Drag & Drop.
Do remember to change the path to where your Java executable is installed.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.jar]
#="jarfile"
[HKEY_CLASSES_ROOT\jarfile\DefaultIcon]
#="C:\\Java\\jdk1.7.0\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\jarfile\shell\open]
#="Run Java Program"
[HKEY_CLASSES_ROOT\jarfile\shell\open\command]
#="\"C:\\Java\\jdk1.7.0\\bin\\java.exe\" -jar \"%1\" %*"
[HKEY_CLASSES_ROOT\jarfile\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
OK, I made it work... The base knowledge is to use DropHandler UUID in the registry. I made a base setting, as follow:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.class]
#="JavaClass"
[HKEY_CLASSES_ROOT\JavaClass\DefaultIcon]
#="C:\\Java\\jdk1.6.0_05\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\JavaClass\shell\open]
#="Run Java class"
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\"C:\\Java\\jdk1.6.0_05\\bin\\java.exe\" \"%1\" %*"
[HKEY_CLASSES_ROOT\JavaClass\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
and... it didn't work!
I just forgot that java.exe wants a class name, not a file name! But I see no way to do that in the registry.
Fortunately, there is a workaround, which still need a script file if we want to be generic, to work on any/all class files (with static main function, of course!). Not batch, I avoid them when I can. I chose to use WSH, as it should be available on any modern Windows system. I also chose to make a JS script, it could have been a VB script as well.
So I made the following script (LaunchJavaClass.js):
if (WScript.Arguments.count() == 0)
{
WScript.StdOut.Write("No parameters");
WScript.Quit(1);
}
var className = WScript.Arguments.Item(0);
//~ WScript.StdOut.Write(className + "\n");
var m = className.match(/^(.*)\\(.+?)\.class$/);
if (m == null)
{
WScript.StdOut.Write("Not a class file");
WScript.Quit(1);
}
var classPath = m[1];
className = m[2];
//~ WScript.StdOut.Write(classPath + " >>> " + className + "\n");
var params = new Array();
for (i = 1; i < WScript.Arguments.count(); i++)
{
params[params.length] = WScript.Arguments.Item(i);
}
var cmd = "cmd /c cd /D " + classPath +
" & C:/Java/jdk1.6.0_05/bin/java.exe " +
className + " " + params.join(" ");
//~ WScript.StdOut.Write(cmd + "\n");
var shell = WScript.CreateObject("WScript.Shell");
//~ var exec = shell.Exec(cmd); // Can be used to get stdout
shell.Run(cmd, 0);
I left some output, not useful in this context, but usable for debugging (run with cscript).
Of course, the path to the JRE must be adjusted.
And I changed the command in the registry, as follow:
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\wscript -b "D:\\_PhiLhoSoft\\WSH\\LaunchJavaClass.js\" %1 %*"
Of course, adjust path, and keep the above other lines.
Now, if I drag'n'drop some files to a .class file, it gets the short file paths as arguments of the main() function.
import java.io.*;
class TestDnD
{
public static void main(String[] args)
{
Writer output = null;
try
{
output = new BufferedWriter(new FileWriter(new File("LogFile.txt")));
for (String arg : args)
{
output.write(arg + "\n");
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
return;
}
finally
{
try { output.close(); } catch (IOException e) {}
}
}
}
I think the first version of the .reg file can be used for something else, eg. to drag'n'drop on .jar files (adapting it, of course).
This technique has limited use: we rarely make one-class programs in Java! But it looked like a good and interesting challenge, so I didn't resist to solve it. Note: you can add stuff like -Djava.ext.dirs="some path;another path" if you ever need to use external libraries (in jar files).
Adding onto Adiel A. If you create a batch file, which launches your a Java window using Swing. You would have the user drop the files onto that window. You could then be able to root through those dropped files.
So there's no way to have windows itself pass the args into main() via drag and drop?