How to set system variables from Java? - java

I need to set certain system variables from within the program. My Google fu is failing me in finding any way to do it. How do I do it? (I am okay with hacky approaches. I need to be able to run this app on Windows, Linux, and Mac.)
Edit:
Adding here my comment from below the post, as it isn't readily visible there:
The best link I could found was this, and it sets the variables only in memory. They do not persist after the program exit.
Edit:
I am writing an installer and need to somehow record at system level that installation happened (along with paths to some directories). The next time user runs the setup, the installer will check if the variables already exist in the system, in which case a user will be given an appropriate warning.
If twiddling with environment variables is not a good idea, what will be the best approach to achieve the above?

Use
following methods of system class
// Get a system property
String dir = System.getProperty("user.dir");
// Set a system property
String previousValue = System.setProperty("application.property", "newValue");
for more details reffer
http://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CF8QFjAA&url=http%3A%2F%2Fdocs.oracle.com%2Fjavase%2Ftutorial%2Fessential%2Fenvironment%2Fsysprop.html&ei=oHLgT6agKcborAf_3L3-DA&usg=AFQjCNGWSWRjk3ityPQqreuwx_O7Bp7kdg&sig2=Y1tfYzdXAmNX-hpB8Z64kw

If you want your environment variables to persist after your program ends, I would suggest you use the Properties class. It can be persisted to a file very easily, and vice versa.

First of all, Properties is a java class that is used to hold properties that maybe needed for your program. The basic properties that you are talking about are provided by the operating system. Not all of these can be changed. If you try, you will get a SecutrityException (You can't change the os.name for instance). The basic properties are read from the memory of the computer (basically) you can add additional variables to this by setting environment variables in the operating system you are using. Such as in Win95 you can add to the autoexec.bat the line:
set BARTENDER_NAME=Carl
This line can go in any batch file and BARTENDER_NAME will equal Carl until you reset it.
In your java program If you add the line
System.out.println(System.getProperty("BARTEDER_NAME"));
You'll get Carl as the output.
In the bash shell on Linux or Unix you'd use
BARTENDER_NAME=Carl
export BARTENDER_NAME
You can create your own set of properties for your java program and store them in a file and load them using the Properties load() method.
Hope this helps
from
http://www.coderanch.com/t/387634/java/java/Permanently-setting-System-property

Ok this is off the top of my head so it's extremely hacky and stuff.
Get hold of a process and run the command line command that will set the system variables. This isn't portable but it should suffice for short term till you find a better solution.

Because there is not a standard solution for this, I would recommend you to use a Factory Pattern for this. It means something like:
envManager = null
if system is Windows
`envManager = WindowsEnvManager`
else if system is Linux
`envManager = LinuxEnvManager`
else if system is Mac
`envManager = macEnvManager`
persistEnvironment(envManager);
and the persistEnvironment method would call the specific functions on EnvManager.

How about using Java Preferences API. That way you would store this kind of data in the Registry if you run on Windows. Simple tutorial here.
You can store the preferences per system or per user and the preferences are persistent as well as you desire.
Edit
Example:
package com.stackoverflow.Q11100967;
import java.util.prefs.Preferences;
/**
* #author maba, 2012-06-20
*/
public class App {
public static void main(String[] args) {
Preferences preferences = Preferences.systemNodeForPackage(App.class);
if (!preferences.getBoolean("installed", false)) {
// Install the stuff...
preferences.putBoolean("installed", true);
preferences.put("version", "1.2.3");
}
}
}
On Windows the preferences will be stored at HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Prefs/com/stackoverflow/Q11100967.
In order for this to work you have to run your process with admin privileges or a similar approach.
Edit2
On Linux the preferences would be stored at /etc/.java/.systemPrefs/com/stackoverflow/Q11100967/ in a file called prefs.xml with the following content:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map MAP_XML_VERSION="1.0">
<entry key="installed" value="true"/>
<entry key="version" value="1.2.3"/>
</map>

Related

install4J, how to correctly pass `updatesUrl` to the updater via the Java preference store?

I am trying to implement multi-channels (deb/beta/stable) update for my app following recommendation made in another SO post.
I have done the following steps:
create a compiler variable mediaID=${compiler:sys.mediaId}
when user of my app changes it release channel preference, the ollowing function is called: mediaID = Variables.getCompilerVariable("mediaID"); Variables.saveToPreferenceStore(Map.of("updatesUrl", xmlURL), mediaID, true);
when user decide to update the following function is called:
ApplicationLauncher.launchApplicationInProcess(UPDATER_APPLICATION_ID, null, ....) but this throws an error (exit value = 1):
Based on the error message, it seems that the updater does not find the updatesUrl variable but just before the call to the function I logged the output of com.install4j.api.launcher.Variables.loadFromPreferenceStore(mediaID, true); and I got as a result {updatesUrl=https://github.com/akasolace/HO/releases/download/dev/updatesDEV.xml} which is the legit URL, so it seems updatesUrl was properly stored.
Now, I am a bit confused on how this should work. Me for example, I am running 2 version of the app in parralel and my setup is something like:
mediaID 1, release channel Stable
mediaID 2, release channel Dev
on my system I will have variable updatesUrl store in 2 differents package something like:
{(1, "updatesUrl"): "...updatesSTABLE.xml", (2, "updatesUrl"): "...updatesDEV.xml"}
Now, in install4J, I added the action "Load installer variable from the Java preference store" and I see package name default to ${compiler:sys.applicationId. This leads to the following question:
I believe applicationID unique for the app and mediaID different for each media. To achieve what I want I believe I need the updater to use as package name, the media ID of the media that called it. I mean if the updater is called from media ID x , I need it to look at the variable "updatesUrl" from package x. Is it doable or am I getting lost?
[Edit] I tried to set package name of "Load installer variable from the Java preference store" to ${compiler:sys.mediaId but this throws instead
Thank you in advance for your help.
with the following stack trace
java.lang.NullPointerException
com.install4j.runtime.installer.Application.runApplicationInProcess(Application.java:64)
com.install4j.runtime.installer.helper.apiimpl.ApplicationLauncherImpl$1.run(ApplicationLauncherImpl.java:57)
Calling com.install4j.api.launcher.Variables.loadFromPreferenceStore does not set installer variables, it returns a map with the variable values. This is intended for use in your application.
What you have to do is to add a "Load installer variables from the Java preference store" action to the "Startup" node in in the update downloader. In that way, the updatesUrl installer variable is actually set to the value from the preference store.

Java how to determine application directory when creating simple installer

I need to create a log file in application directory. Currently I'm using hard-coded absolute path, like
public class Transformer
{
static String LOG_DIR = "ABSOLUTE_PATH_TO_THE_APPLICATION_DIRECTORY";
File log = new File(log_DIR, "log");
}
If someone else have my code, he will has to go the source code, change the LOG_DIR and then recompile. I just know a little about GNU make. My question is, how can I create a "installer" that works like:
./config
make install
and what's the standard/better way of achieving this?
Currently I have:
String path = Transformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()
Best way to load application settings
With 2, I think I can use a shell script to generate a .properties file.
update
Sorry about the confusing "log file", but I actually mean a regular file, it just happen to containing some sort of log information.
You could define the "app.logs.dir" property on application start and refer to it in the log4j configuration:
main(String[] args) {
...
final String appRootDir = /* Detect app location. */;
System.setProperty("app.logs.dir", appRootDir);
// Now we can use the logger.
...
}
And in "log4j.properties":
log4j.appender.filelog.file=${app.logs.dir}/myapp.log
UPDATE
Option #1, the getProtectionDomain() one, might not work depending on the JVM's SecurityManager settings so the best way probably is option #2 - your installer script should store the application installation location in the application configuration file.
And to keep the number of component references low you can read the configuration file into System properties just like I showed above.
File log = new File(System.getProperty("app.logs.dir"), "log");

Java Create Undeletable File

Is there any method to create a file in java that cannot be deleted.
I have googled it and found processes involving the cmd.
However, I require a pure "java" way that can be done on any platform.
Thanks in advance.
Thank you for your help.
I finally got it right.
I used the following code to deny access to user
public static void main() throws IOException
{
Path file = Paths.get("c:/b.txt");
AclFileAttributeView aclAttr = Files.getFileAttributeView(file, AclFileAttributeView.class);
//System.out.println();
UserPrincipalLookupService upls = file.getFileSystem().getUserPrincipalLookupService();
UserPrincipal user = upls.lookupPrincipalByName(System.getProperty("user.name"));
AclEntry.Builder builder = AclEntry.newBuilder();
builder.setPermissions(EnumSet.of(AclEntryPermission.APPEND_DATA, AclEntryPermission.DELETE, AclEntryPermission.DELETE_CHILD, AclEntryPermission.EXECUTE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.READ_NAMED_ATTRS, AclEntryPermission.SYNCHRONIZE, AclEntryPermission.WRITE_ACL, AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.WRITE_NAMED_ATTRS, AclEntryPermission.WRITE_OWNER));
builder.setPrincipal(user);
builder.setType(AclEntryType.DENY);
aclAttr.setAcl(Collections.singletonList(builder.build()));
}
Try the method setPosixFilePermissions() and set the permissions to read only for all the classes of users. Refer this - http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#setPosixFilePermissions%28java.nio.file.Path,%20java.util.Set%29
If you want to create a file that can't be accidentally overwritten, then look at the various answers to this: How do i programmatically change file permissions?
If you want to create a file that the current program cannot delete at all (but a privileged one could), it might be possible by setting permissions appropriately on the parent directory, or possibly using SELinux Mandatory Access Control cleverness.
If you want to create a truly undeleteable file, then you are out of luck. I am not aware of any operating system that supports creation of files that can never be deleted. It would be an "anti-feature".
I would also agree with #Teifi's comment. Create a file that cannot ever be deleted on the user's machine is not acceptable ... unless done by, or with the authorization of the system's administrators. I would call any software that did that "malicious" too.

Strategy to consolidate Java webapp configuration files for multiple deployments

I apologize if this is a duplicate, I couldn't find anything describing exactly what I wanted. I'm building a webapp that has a number of different properties that need to change depending on the environment in addition to a number of .properties configuration files that need to change as well. Right now I have a global enum (DEVELOPMENT, STAGING, and PRODUCTION) that is used to determine which string constants are used in the application and then I utilize a bunch of comments in the configuration files to switch between database servers, etc. There has got to be a better way to do this...I'd ideally like to be able to make one change in one file (A large block comment would be fine...) to adjust these configurations. I saw this post where the answer is to utilize JNDI which I really like, but it would seem as though I would need to call that from a servlet that starts up or a bean that gets initialized on start in order to use it for my log4j or JDBC configuration files.
Does anybody have any strategies for dealing with this?
Thanks!
I'm not sure if this strategy will apply to your situation, but in the past I've successfully used our build tool (ant in that case) to build different war files depending on the profile. So you would have multiple log4j configuration files in your source tree, and then delete the ones you don't want from the final build depending on the profile that was used to build it.
Traceability becomes slightly hard (i.e. difficult sometimes to figure out which one was used to build it), but it's a very clean solution, from your code perspective, since it's all done in your build script.
We store all our default configuration values in a single XML file. During deployment we apply an XML patch (RFC-5261) with values specific to the environment.
https://www.rfc-editor.org/rfc/rfc5261
http://xmlpatch.sourceforge.net/
I am going to assume that your properties files are made up of 95% name=value pairs that are identical across all your deployment environments and 5% of name=value pairs that change from one deployment environment to another.
If this assumption is correct, then you could try something like the following pseudocode.
void generateRuntimeConfigFiles(int deploymentMode)
{
String[] searchAndReplacePairs;
if (deploymentMode == Constants.PRODUCTION) {
searchAndReplacePairs = ...
} else if (deploymentMode == Constants.STAGING) {
searchAndReplacePairs = ...
} else { // Constants.DEVELOPMENT
searchAndReplacePairs = ...
}
String[] filePairs = new String[] {
"log4j-template.properties", "log4j.properties",
"jdbc-template.properties", "jdbc.properties",
"foo-template.xml", "foo.xml",
...
};
for (int i = 0; i < filePairs.length; i += 2) {
String inFile = filePairs[i + 0];
String ouFile = filePairs[i + 1];
searchAndReplaceInFile(inFile, outFile,
searchAndReplacePairs);
}
}
Your application calls generateRuntimeConfigFiles() before initialising anything else that might rely on properties/XML files.
Now the only problem you have to deal with is how to store and retrieve different settings for searchAndReplacePairs. Perhaps you could obtain them from files with names such as production.properties, staging.properties and development.properties.
If the above approach is appealing to you, then email me for the source code of searchAndReplaceInFile() to save you having to re-invent the wheel. You can find my email address from the "info" box in my Stackoverflow profile.
I suggest using Apache Commons Configuration. It provides all the plumbing for dealing with different configurations depending on your environment.
http://commons.apache.org/configuration

How to disable Java security manager?

Is there any way to completely disable Java security manager?
I'm experimenting with source code of db4o. It uses reflection to persist objects and it seems that security manager doesn't allow reflection to read and write private or protected fields.
My code:
public static void main(String[] args) throws IOException {
System.out.println("start");
new File( DB_FILE_NAME ).delete();
ObjectContainer container = Db4o.openFile( DB_FILE_NAME );
String ob = new String( "test" );
container.store( ob );
ObjectSet result = container.queryByExample( String.class );
System.out.println( "retrieved (" + result.size() + "):" );
while( result.hasNext() ) {
System.out.println( result.next() );
}
container.close();
System.out.println("finish");
}
Output:
start
[db4o 7.4.68.12069 2009-04-18 00:21:30]
AccessibleObject#setAccessible() is not available. Private fields can not be stored.
retrieved (0):
finish
This thread suggests modifying java.policy file to allow reflection but it doesn't seem to work for me.
I'm starting JVM with arguments
-Djava.security.manager -Djava.security.policy==/home/pablo/.java.policy
so specified policy file will be the only policy file used
The file looks like this:
grant {
permission java.security.AllPermission;
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};
I spent last 3 hrs on this and don't have any ideas how to make this work.
Any help appreciated.
You could try adding this to the main() of your program:
System.setSecurityManager(null);
Worked for me for a "trusted" WebStart application when I was having security manager issues. Not sure if it will work for your db4o case, but it might be worth a try.
EDIT: I'm not suggesting that this is a general solution to security manager problems. I was just proposing it as a way to help debug the original poster's problem. Clearly, if you want to benefit from a security manager then you should not disable it.
Do you really have two '=' signs in your java.security.policy command line option? That won't work. Make sure you are setting the property as
-Djava.security.policy=/home/pablo/.java.policy
To actually disable the SecurityManager, simply leaving off the java.security.manager system property altogether should be enough.
Update: As I was reading the documentation for policy files to learn more about the "==" syntax, I noticed that unless the policy file is in the current working directory, it needs to be specified as a URL (including scheme). Have you tried prefixing the policy path with the "file:" scheme?
I was also puzzled because (assuming you are running as user "pablo"), it looks like that policy should be loaded by default from your home directory, so you shouldn't need to specify it at all. On the other hand, if you are not running as the user "pablo", maybe the file is not readable.
I found this example of how to make private fields and methods accessible to your code. Basically, it distills down to the use of Field.setAccessible(true) and Method.setAccessible(true)
Field example:
Field privateStringField = PrivateObject.class.
getDeclaredField("privateString");
privateStringField.setAccessible(true);
Method example:
Method privateStringMethod = PrivateObject.class.
getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
You could also look at using Groovy with your Java code as it (currently) circumvents much of the access level restrictions of Java code. Although, this message board posting seems to suggest this 'feature' may change in future versions of Groovy.

Categories

Resources