best way to store data in Java like pickle - java

Basically, I just want to save two integeres into a File, so that i can reuse them the next time the programm starts. Id like to do it like pickle in python, beacuse writing it just into a txt file is cumbersome. I have read some articles and other questions wehere they say I should use Java serializatio or XML or JSON, but Im not sure wheter that is the right thing in my case. Id like to use the easiest way.
thank you very much in advance for trying to solve my problem! <3

You could use serialization, XML or JSON (usually with additional libraties). An easy way to store configuration data in files is by using Java property files which are supported by the JRE without any additional dependencies. Property files are text files and have a simple key=value syntax, see below. To write two values to a property file you can do
String prop1 = "foo";
String prop2 = "bar";
try (OutputStream output = new FileOutputStream("config.properties")) {
Properties prop = new Properties();
// set the properties value
prop.setProperty("prop1", prop1);
prop.setProperty("prop2", prop2);
// save properties to project root folder
prop.store(output, "my app's config file");
} catch (IOException io) {
io.printStackTrace();
// TODO: improve error handling
}
which should give you something like
#my app's config file
#Sat Feb 29 12:29:27 CET 2020
prop2=bar
prop1=foo
And to load it:
try (InputStream input = new FileInputStream("config.properties")) {
Properties prop = new Properties();
// load a properties file
prop.load(input);
// get the property value and print it out
String prop1 = prop.getProperty("prop1");
String prop2 = prop.getProperty("prop2");
System.out.println("prop1 = " + prop1);
System.out.println("prop2 = " + prop2);
} catch (IOException ex) {
ex.printStackTrace();
// TODO: improve error handling
}
For integer values you would need some type conversion, e.g. the first two lines would be
String prop1 = Integer.toString(23);
String prop2 = Integer.toString(42);
and reading the properties then becomes
int prop1 = Integer.parseInt(prop.getProperty("prop1"));
int prop2 = Integer.parseInt(prop.getProperty("prop2"));
This solution does not scale well in case the number of properties increases or there are frequent changes. For a more generic procedure, see this post: Get int, float, boolean and string from Properties

Related

Save a variable when the server is off

In fact I am making a Minecraft plugin and I was wondering how some plugins (without using DB) manage to keep information even when the server is off.
For example if we make a grade plugin and we create a different list or we stack the players who constitute each. When the server will shut down and restart afterwards, the lists will become empty again (as I initialized them).
So I wanted to know if anyone had any idea how to keep this information.
If a plugin want to save informations only for itself, and it don't need to make it accessible from another way (a PHP website for example), you can use YAML format.
Create the config file :
File usersFile = new File(plugin.getDataFolder(), "user-data.yml");
if(!usersFile.exists()) { // don't exist
usersFile.createNewFile();
// OR you can copy file, but the plugin should contains a default file
/*try (InputStream in = plugin.getResource("user-data.yml");
OutputStream out = new FileOutputStream(usersFile)) {
ByteStreams.copy(in, out);
} catch (Exception e) {
e.printStackTrace();
}*/
}
Load the file as Yaml content :
YamlConfiguration config = YamlConfiguration.loadConfiguration(usersFile);
Edit content :
config.set(playerUUID, myVar);
Save content :
config.save(usersFile);
Also, I suggest you to make I/O async (read & write) with scheduler.
Bonus:
If you want to make ONE config file per user, and with default config, do like that :
File oneUsersFile = new File(plugin.getDataFolder(), playerUUID + ".yml");
if(!oneUsersFile.exists()) { // don't exist
try (InputStream in = plugin.getResource("my-def-file.yml");
OutputStream out = new FileOutputStream(oneUsersFile)) {
ByteStreams.copy(in, out); // copy default to current
} catch (Exception e) {
e.printStackTrace();
}
}
YamlConfiguration userConfig = YamlConfiguration.loadConfiguration(oneUsersFile);
PS: the variable plugin is the instance of your plugin, i.e. the class which extends "JavaPlugin".
You can use PersistentDataContainers:
To read data from a player, use
PersistentDataContainer p = player.getPersistentDataContainer();
int blocksBroken = p.get(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER); // You can also use DOUBLE, STRING, etc.
The Namespaced key refers to the name or pointer to the data being stored. The PersistentDataType refers to the type of data that is being stored, which can be any Java primitive type or String. To write data to a player, use
p.set(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER, blocksBroken + 1);

Java: Read Line From File Based on Start of Line

I know how to read from file using Java. What I want to do is read a specific line which starts with specific text.
What I plan on doing is storing certain program settings in a txt file so I can retrieve them quickly when I exit/restart program.
For example, the file may look something like this:
First Name: John
Last Name: Smith
Email: JohnSmith#gmail.com
Password: 123456789
The : would be the delimiter and in the program I want to be able to retrieve specific values based on the "key" (such as "First Name", "Last Name" and so on).
I know I could store it to DB but I want to write it quickly to test my program without going through hassle of writing it to DB.
Have a look at java.util.Properties. It does everything you ask for here, including parsing the file.
example code:
File file = new File("myprops.txt");
Properties properties = new Properties();
try (InputStream in = new FileInputStream (file)) {
properties.load (in);
}
String myValue = (String) properties.get("myKey");
System.out.println (myValue);
Note: if you want to use a space in your property key, you have to escape it. For example:
First\ Name: Stef
Here is documentation about the syntax of the properties file.
What I want to do is read a specific line which starts with specific text.
Read from the start of the file, skipping all the lines you don't need. There is no simpler way. You can index you file for fast access, but you have scan the file at least once.
You can use Properties to retrieve both key and value from file.
Reading data from text file using Properties class
File file = new File("text.txt");
FileInputStream fileInput = new FileInputStream(file);
Properties properties = new Properties();
properties.load(fileInput);
fileInput.close();
Enumeration enuKeys = properties.keys();
while (enuKeys.hasMoreElements()) {
String key = (String) enuKeys.nextElement();
String value = properties.getProperty(key);//with specific key
System.out.println(key + ": " + value);//both key and value
}
You can retrieve specific value based on the key.
System.out.println(properties.getProperty("Password"));//with specific key
With Java 8, you can also read your file into a map this way:
Map<String, String> propertiesMap = Files.lines(Paths.get("test.txt")) // read in to Stream<String>
.map(x -> x.split(":\\s+")) // split to Stream<String[]>
.filter(x->x.length==2) // only accept values which consist of two values
.collect(Collectors.toMap(x -> x[0], x -> x[1])); // create map. first element or array is key, second is value

Can I use a Java property value to reference a Java variable or constant?

Assume I have a Java constant called DARK_CYAN that is equal to an RBG hex value. I'd like to allow a user to utilize a JSON or XML file to configure my program. The configuration file would have key/value pairs such as "WINDOW_BACKGROUD:DARK_CYAN". When the program reads in the configuration properties, it would see that the window background needs to be set to DARK_CYAN. Is it possible to reference the Java constant DARK_CYAN using a string value of "DARK_CYAN"?
Sure, you can use Properties class for that. A good example from mkyong website:
package com.mkyong.properties;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
public class App {
public static void main(String[] args) {
Properties prop = new Properties();
OutputStream output = null;
try {
output = new FileOutputStream("config.properties");
// set the properties value
prop.setProperty("database", "localhost");
prop.setProperty("dbuser", "mkyong");
prop.setProperty("dbpassword", "password");
// save properties to project root folder
prop.store(output, null);
} catch (IOException io) {
io.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
How many constants are you keeping track of? You'll need to parse the JSON and explicity create a Map between the string value of the constant and the constant itself. So, for example (assuming that the RBG hex value is a string):
Map<String, String> constantMap = new HashMap<String, String>();
Map.put("DARK_CYAN", DARK_CYAN);
Then, you can parse the JSON and do a constantMap.get(parsedStrname) to get the constant value referenced.
Don't understand why would use xml to store the properties.
The common approach to define properties in form "propertyKey=propertyValue".
To load from properties file:
public Properties loadPropertiesResource(String fileLocation) {
Properties properties = new Properties();
InputStream inStream = Utils.class.getClassLoader().getResourceAsStream(fileLocation );
if (inStream == null)
throw new IOException("Properties Files is Missing - " + fileLocation );
properties.load(inStream);
return properties;
}
Then you just:
loadPropertiesResource("app.properties").getProperty("DARK_CYAN");
Technically, you can achieve this with help of reflection.
Once you have parsed your config file (JSON or XML) using the required parsing library, you would have an object containing the properties from your file. One such property would be WINDOW_BACKGROUD as you mentioned, with the value DARK_CYAN. Now, if you don't want to hard-code the lookup, you can use reflection as below:
//winBackground value is read from the property file
String winBackground = (String) jsonObject.get("WINDOW_BACKGROUD");
Field f = YourConstantsClass.class.getField(winBackground);
//assuming that the value is of 'static int' type
int val = f.getInt(null);
If the constant value is of any other type such as 'double', use appropriate get method
or have if-else conditions to check the types. I hope, the constants are declared static final in your class.
EDIT: btw, the approach that you have chosen for maintaining value names in the param file and then map the value names to actual values in your constant class is not a good design, as you need to change your class when you want to support new values. Also, you would need to assume that the value name (e.g. DARK_CYAN) that you use in the prop file would be defined in your constants class. If it is not, you would have a reflection exception.

changing log4j file name programatically in osgi maven bundle not working

Im developing a maven-osgi bundle and deploying in karaf.. In that, a piece of code, should get .cfg files from the karaf/etc and im programatically changing them at runtime.. writeTrace() is invoked within 'for loop' from another class. So that I can create different files and corresponding logging should go in to that file.
public void writeLog(int i,String HostName) {
StringBuilder sb = new StringBuilder();
sb.append("\n HEADER : \n");
....
String str = sb.toString();
String logfile = ("/home/Dev/" + HostName + i);
logger = LoggerFactory.getLogger("TracerLog");
updateLog4jConfiguration(logfile);
logger.error(str + i);}
public void updateLog4jConfiguration(String logFile) {
Properties props = new Properties();
try {
// InputStream configStream = getClass().getResourceAsStream(
// "/home/Temp-files/NumberGenerator/src/main/java/log4j.properties");
InputStream configStream = new FileInputStream("etc/org.ops4j.pax.logging.cfg");
props.load(configStream);
System.out.println(configStream);
configStream.close();
} catch (IOException e) {
System.out.println("Error: Cannot laod configuration file ");
}
props.setProperty("log4j.appender.Tracer.File", logFile);
LogManager.resetConfiguration();
PropertyConfigurator.configure(props);
}
and I am able to see new files created with hostname such as (hostname_1 , hostname_2, etc..) but logging happens only at actual appender configured at karaf/etc... thaat is log.txt..
log4j.logger.TracerLog=TRACE,Tracer
log4j.appender.Tracer=org.apache.log4j.RollingFileAppender
log4j.appender.Tracer.MaxBackupIndex=10
log4j.appender.Tracer.MaxFileSize=500KB
log4j.appender.Tracer.File=/home/Dev/log.txt
I got struck in this error.. Dont know whether it has to do something with the karaf or problem with code..???
Why aren't you just using the ConfigurationAdminService for this, instead of altering the file?
Just reference the configuration admin service from the registry and take the configuration with the PID org.ops4j.pax.logging.
With this approach you will have all configuration properties available for your proposal and it is in your code to alter this. It's also possible for you to add new configuration entries. In the end the combination of ConfigurationAdminService and the felix FileInstaller will even persist your changes back to the configuration file.
Btw. did you know that there is a shell command for configuring configurations, so actually also to alter the configuration for the org.ops4j.pax.logging service?
Just do a:
config:list
to retrieve all configurations available
and a
config:list "(service=org.ops4j.pax.logging)"
to retrieve just this information.

need to extract key from properties file

see i have one PROPERTIES File now i have to write a program for extraction of all keys from that file
1000012001 = Title
1000012002 = Status
1000012003 = Start Date
1000012004 = End Date
1000012005 = Date
1000012006 = Name
1000012007 = Description
1000012008 = Sr No
1000012009 = Action
1000012010 = Add
1000012011 = COMPASS Alerts
1000012012 = All
1000012013 = Apply
like in given example i have to extract keys like 100012001---100012013 from that file keys may not be in one sequence and keys need to store in hashmap or arraylist
so please help me
Properties props = new Properties();
props.load(in); // create input stream for your file.
// from now you have Properties object with your data.
// since properties extends Hashtable the task is done.
// if you still need keys in list, say
List<Object> keys = new ArrayList<Object>(props.keySet());
I hope this helps although the question does not seem very clear for me.
You can use java.util.Properties which extends Hashtable.
Properties props=new java.util.Properties();
props.load(inputStream);

Categories

Resources