make a property loader for an android application - java

I am developing an android application and there are several variables that i might need to change wihtout wanting to recompile and deploy the android application into the android smartphone.
In java i would do a propertyloader like the following i have done in java before:
public class PropertyLoader {
private static Properties props = null;
/**
* Method initializes the PropertyLoader by reading all configuration settings
* from the RememberMeServer.conf file.
*/
public static void initializeProperties() {
String propFile = getCatalinaDirectory() + "RememberMeServer.conf";
System.out.println(propFile);
File configFile = new File(propFile);
InputStream inputStream = null;
try {
inputStream = new FileInputStream(configFile);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
props = new Properties();
try {
props.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns a string value from the configuration file.
*
* #param key a string which represents the key of the requested value in the configuration file
* #return the value of the requested key from the property file as a string or null if the
* requested key could not be found.
*/
public static String getStringValue(String key) {
return props == null ? null : props.getProperty(key);
}
/**
* Returns an int value from the configuration file.
*
* #param key a string which represents the key of the requested value in the configuration file
* #return the value of the requested key from the property file as an int or null if the
* requested key could not be found.
*/
public static Integer getIntValue(String key) {
return props == null ? null : Integer.valueOf(props.getProperty(key));
}
/**
* Returns the directory of the project�s workspace as a string
*
* #return Returns the directory of the project�s workspace as a string
*/
public static String getWorkspaceDirectory() {
URL url = PropertyLoader.class.getClassLoader().getResource(
"hibernate.cfg.xml");
return url.getFile().substring(0,
url.getFile().lastIndexOf("hibernate.cfg.xml"));
}
/**
* Returns the directory of the servlet container catalina directory as a string
*
* #return Returns the directory of the servlet container catalina directory as a string
*/
public static String getCatalinaDirectory() {
String workspace = getWorkspaceDirectory();
return workspace
.substring(0, workspace.lastIndexOf("RememberMeServer"));
}
}
Although in android there is something called SharedPreferences which i already use in my application. Although i never use the SharedPreferences to change variable information directly in the file but only from the application's code.
What is the best alternative in an android application?
Because what i want to achieve is, to me, better represented by a property loader which saves things that i do not want to hard code in my java code.

You can use xml file to store your configuration and access the same way as key being the tag and value being the tag value.
For example-
Path = yourapp/res/xml/RememberMeServer.xml
RememberMeServer.xml contents -
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>day</key>
<integer>1</integer>
<key>hour</key>
<integer>12</integer>
<key>minute</key>
<integer>10</integer>
<key>second</key>
<integer>14</integer>
<key>background</key>
<string>Graphic_6_7_Red</string>
<key>Online</key>
<true/>
</dict>
</plist>
Then to access and use the key-value -
Class Code -
String day, hour, minute, second, background;
boolean status;
int resId = getResources().getIdentifier("xml/" + RememberMeServer,
"string", getActivity().getPackageName());
XmlPullParser xpp0 = getResources().getXml(resId);
XMLData xml = new XMLData(xpp0);
day = (xml.getValue("day"));
hour= (xml.getValue("hour"));
second= (xml.getValue("second"));
minute= (xml.getValue("minute"));
background= (xml.getValue("Graphic_6_7_Red"));
status= (xml.checkFieldPresence("Online"));
Class XMLData.java -
(This class contains the logic of accessing value by key)
public String getValue(String key) {
int start = this.xmldoc.indexOf("<key>"+ key + "</key>");
if(start == -1)
return "";
String xmldoc2 = this.xmldoc.substring(start);
start = xmldoc2.indexOf("</key>");
xmldoc2 = xmldoc2.substring(start);
xmldoc2 = xmldoc2.substring(6);
start = xmldoc2.indexOf(">");
xmldoc2 = xmldoc2.substring(start + 1);
int end = xmldoc2.indexOf("</");
xmldoc2 = xmldoc2.substring(0, end);
return xmldoc2;
}
public boolean checkFieldPresence(String key)
{
int start = this.xmldoc.indexOf(key + "</key>");
if(start == -1)
return false;
else
return true;
}
NOTE:
You can change any value for any key in your file RememberMeServer.xml.
This provides the flexibility that you don't have to worry about getting the saved value of the key. Whatever will be the value, the methods return those values by their keys.
SharedPreferences is also a good thing but as you told that you have lots of variables that change a lot so the best solution is to put them all in a xml file and access them when needed. You can change any value according to the requirement and still be content specific. The whole logic is centered within a single xml file and you can look and change just a single file to modify any value.

Option 1: App Resources
What I really don't understand is why you are not using the normal app resources? From what you describe that would be exactly what you are looking for. You can specify all kinds of values in there and things like ip and port and other important values should always be in the resources anyway. If you for example need an integer somewhere you can just define an integer in res/values/ints.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="some_integer">27</integer>
<integer name="another_integer">42</integer>
</resources>
Or a string can be defined in res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="some_string">Some Text</integer>
<string name="another_string">qwerty</integer>
</resources>
And all values you define in those resources can be loaded later on at any time:
Resources resources = getResources();
int someInteger = resources.getInteger(R.integer.some_integer);
String someString = resources.getString(R.string.some_string);
You can find more information about the app resources in the official documentation.
Option 2: SharedPreferences
Another option are of course the SharedPreferences. Internally the SharedPreferences are saved to a File. But I guess that this is not what you are looking for since it would still require you to hardcode all initial values.
Option 3: Something fancy
If you want I can also slap something fancy together like this:
#Resource(id = R.string.some_string)
private String someString;
#Resource(id = R.integer.some_integer)
private int someInteger;
As you can see this uses reflection and annotations to load resource values. Could be very convenient and might be exactly what you are looking for. You load all the annotated values from one Object by calling this:
ResourceLoader.load(context, object);
The source code of this ResourceLoader is nothing fancy, but currently it only supports loading string and integer resources, but it should be no problem to expand this:
public class ResourceLoader {
// This is the definition of the #Resource annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Resource {
public int id();
}
public static void load(Context context, Object target) {
final Class<?> cls = target.getClass();
// This gets all declared fields, meaning all inherited fields are ignored
final Field[] fields = cls.getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
final Class<?> type = field.getType();
// We check if the Annotation is present
if(field.isAnnotationPresent(Resource.class)) {
final Resource annotation = field.getAnnotation(Resource.class);
final int id = annotation.id();
// And if it is present use the id and type of the field to load
// the correct resource
final Object value = loadValue(context, id, type);
try {
// Finally we set the new value to the field
field.set(target, value);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not set resource value to field " + field, e);
}
}
}
}
private static Object loadValue(Context context, int id, Class<?> type) {
final Resources resources = context.getResources();
if(int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)) {
return resources.getInteger(id);
}
if(String.class.isAssignableFrom(type)) {
return resources.getString(id);
}
throw new IllegalStateException("Type \"" + type + "\" is not supported!");
}
}

Related

Config-file for JavaFX-Project

I would like to add a file or class to my JavaFX project that only contains the configuration data of the project, e.g. the access data for the database, system paths etc. How would you do this?
Just write everything in a normal class? There is definitely a better way, right?
You're right, of course I'll be happy to do that.
First I created a property file in the project folder and call it app.properties:
db_url=jdbc:mysql://localhost:3306/db name
db_user=user name
db_pwd=secret password
instructions_folder=/home/username/documents/
Then I created a class that loads the properties and makes them available throughout the project.
public class AppProperties {
// FILENAME = Path to properties-file
// Store and protect it where ever you want
private final String FILENAME = "app.properties";
private static final AppProperties config_file = new AppProperties();
private Properties prop = new Properties();
private String msg = "";
private AppProperties(){
InputStream input = null;
try{
input = new FileInputStream(FILENAME);
// Load a properties
prop.load(input);
}catch(IOException ex){
msg = "Can't find/open property file";
ex.printStackTrace();
}finally{
if (input != null){
try{
input.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
public String getProperty (String key){
return prop.getProperty(key);
}
public String getMsg () {
return msg;
}
// == Singleton design pattern == //
// Where ever you call this methode in application
// you always get the same and only instance (config_file)
public static AppProperties getInstance(){
return config_file;
}
}
In the DBUtilitis class, where I do my database queries, I now load the properties into final variables and use them in the query methods.
private static final String db_url = AppProperties.getInstance().getProperty("db_url");
private static final String db_user = AppProperties.getInstance().getProperty("db_user");
private static final String db_pwd = AppProperties.getInstance().getProperty("db_pwd");
If I have not completely misunderstood this, the advantage of property files is that they can be stored and protected somewhere on the server. I hope the solution is not completely wrong - it works well anyway. I am always happy to receive suggestions and / or improvements.

Logging the PID in log4j2 without using thread context

I need to include the PID in my log4js logs. I see many examples which use the thread context. However, these need to be set on each individual thread created. I am constrained against doing this.
I need a solution that, either, does not use the thread context, or, can set the PID on all thread contexts, for any thread that may be created, from any arbitrary class.
Please create a feature request on the Log4j2 issue tracker to make this a built-in feature.
For now, you can create a custom plugin. See code below. This will allow you to specify %pid in the pattern layout (similar to %m for the message).
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
#Plugin(name = "ProcessIdPatternConverter", category = "Converter")
#ConverterKeys({ "pid", "processId" })
public final class ProcessIdPatternConverter extends LogEventPatternConverter {
private final String pid;
private ProcessIdPatternConverter(String[] options) {
super("Process ID", "pid");
String temp = options.length > 0 ? options[0] : "???";
try {
// likely works on most platforms
temp = ManagementFactory.getRuntimeMXBean().getName().split("#")[0];
} catch (final Exception ex) {
try {
// try a Linux-specific way
temp = new File("/proc/self").getCanonicalFile().getName();
} catch (final IOException ignoredUseDefault) {}
}
pid = temp;
}
/**
* Obtains an instance of ProcessIdPatternConverter.
*
* #param options users may specify a default like {#code %pid{NOPID} }
* #return instance of ProcessIdPatternConverter.
*/
public static ProcessIdPatternConverter newInstance(final String[] options) {
return new ProcessIdPatternConverter(options);
}
#Override
public void format(final LogEvent event, final StringBuilder toAppendTo) {
toAppendTo.append(pid);
}
}
See the manual for more details on how Log4j2 plugins work.
One way to let Log4j2 recognize your plugin is by specifying the package name of the plugin class in the packages attribute of the configuration:
<Configuration status="trace"
packages="com.myorg.mypluginpackage">
(Trace switches on Log4j2 internal debugging to help with troubleshooting.)

How to set a default value to variables in Guice

Like we do the following in Spring
#Value("${varName:0}")
int varName;
Is there a way to do this using Google Guice?
In Guice you would annotate the method and make it optional. You then just assign the default value. If no property is there to be injected, it will be the default value.
For example:
public class TestModule3 extends AbstractModule {
#Override
protected void configure() {
// Properties p = new Properties();
// p.setProperty("myValue", "12");
// Names.bindProperties(binder(), p); // this binds the properties that usually come for a file
bind(Manager.class).to(ManagerImpl.class).in(Singleton.class);
}
public static interface Manager {
public void talk();
}
public static class ManagerImpl implements Manager {
#Inject(optional = true)
#Named("myValue")
int test = 0;
#Override
public void talk() {
System.out.println(test);
}
}
public static void main(String[] args) {
Manager instance = Guice.createInjector(new TestModule3()).getInstance(Manager.class);
instance.talk();
}
}
This will print "0" for you, because I commented out the property binding. If you remove the comments, it will bind the value 12 to the String myValue. The inject annotation takes care of the rest.
Hope that helps,
EDIT:
As #TavianBarnes pointed out, Guice 4+ has an OptionalBinder. I tried this for your usecase and could not make it work out of the box.
It appears that OptionalBinding is very useful for classes (actual instances), not for properties. Here is why:
You have to know all the properties in advance and bind them to their defaults. It is easy to forget them. The example shown by OP also shows that he does not know if he has the property available (based on the name).
Default implementation of property bindings don't work in combo with the OptionalBinding.
So the way you can make that work is like this:
OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("myValue"))).setDefault()
.toInstance("777");
Properties p = new Properties();
p.setProperty("myValue", "12");
// use enumeration to include the default properties
for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
String propertyName = (String) e.nextElement();
String value = p.getProperty(propertyName);
OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named(propertyName))).setBinding()
.toInstance(value);
}
I had to copy the Named binding code and change it to support optional bindings.
In summary:
I would prefer to use the optional=true flag + default value in code for properties.
Use the OptionalBinding for actual classes that can be optional.
Finally, there is one more thing you could do - this is my solution in my code. I have a similar requirement (not the optional, but default values).
I want:
Bind my properties
Check if my properties are a variable
Replace the variable
If the variable is not available set a default
Apache offers a handy library for this already which I reuse. This is how my properties look like:
myProperty=${ENV_VAR_NAME:-600}
This is the default annotation of how to define a default value.
The above property says:
Use the evnironment variable "ENV_VAR_NAME".
If "ENV_VAR_NAME" is not set, use the value "600"
Then I bind it as follows:
InputStream resourceAsStream = getClass().getResourceAsStream(path);
if(resourceAsStream == null) {
throw new IllegalArgumentException("No property file found for path: " + path);
}
try {
p.load(resourceAsStream);
EnvironmentVariableSubstitutor envSubstitutor = new EnvironmentVariableSubstitutor(false);
Set<Object> keys = p.keySet();
for(Object k : keys) {
String property = p.getProperty(k.toString());
property = envSubstitutor.replace(property);
p.put(k, property);
}
} catch (IOException e) {
throw new IllegalStateException("Could not load properties", e);
} finally {
try {
resourceAsStream.close();
} catch (IOException e) {
log.error("Could not close stream for resource " + path);
}
}
Names.bindProperties(binder(), p);
What this code does is:
Load the properties from a resource file
Use the EnvironmentVariableSubstitutor to process the values of the properties and overwrite the result. (see loop)
finally, bind the modified properties to their names.
These are all the solutions I can come up with at short notice :) let me know if something's unclear
Edit 2:
there is some info on OptionalBindings and properties + how to handle default values in this google thread as well: https://groups.google.com/forum/#!topic/google-guice/7Ga79iU_sb0
Artur

Should YamlConfiguration objects be closed?

I've been working on a plugin that requires a fair amount of data being stored.
I have it being stored in a custom config file I found online that works basically the same as the default config.
The problem I'm having is that I am not sure how to actually close the file or if I even need to, as I know little about yaml configurations.
The code for the template I used is below.
I'm also curious as to advice on how I should store larger amounts of data in the future.
public class CustomConfig {
//store name of file to load/edit
private final String fileName;
//store plugin, to get file directory
private final JavaPlugin plugin;
//store actual hard disk file location
private File configFile;
//store ram file copy location
private FileConfiguration fileConfiguration;
//constructor taking a plugin and filename
public CustomConfig(JavaPlugin plugin, String fileName) {
//ensure plugin exists to get folder path
if (plugin == null)
throw new IllegalArgumentException("plugin cannot be null");
//set this classes plugin variable to the one passed to this method
this.plugin = plugin;
//get name of file to load/edit
this.fileName = fileName;
//get directory/folder of file to load/edit
File dataFolder = plugin.getDataFolder();
if (dataFolder == null)
throw new IllegalStateException();
//load config file from hard disk
this.configFile = new File(plugin.getDataFolder(), fileName);
reloadConfig();
}
public void reloadConfig() {
//load memory file from the hard copy
fileConfiguration = YamlConfiguration.loadConfiguration(configFile);
// Look for defaults in the jar
File configFile = new File(plugin.getDataFolder(), fileName);
if (configFile != null) {
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(configFile);
fileConfiguration.setDefaults(defConfig);
}
}
public FileConfiguration getConfig() {
if (fileConfiguration == null) {
this.reloadConfig();
}
return fileConfiguration;
}
public void saveConfig() {
if (fileConfiguration == null || configFile == null) {
return;
} else {
try {
getConfig().save(configFile);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + configFile, ex);
}
}
}
public void saveDefaultConfig() {
if (!configFile.exists()) {
this.plugin.saveResource(fileName, false);
}
}
}
No. You do not have to close YamlConfiguration objects.
While the default config (JavaPlugin.getConfig()) is bound to the lifecycle of the plugin, custom ones are disposed when any other Java object is, i.e. when the garbage collector determines that there are no more references pointing to them in the code.
You don't need to close the config. It's not a BufferedWriter. The config keeps all of the data in the memory until the server shuts down. This means that if you change something in the config during the time your plugin is enabled, you will need to use your reloadConfig() method. The only clean up you need to do after using the FileConfiguration#set(String, Object) method is to use FileConfiguration#saveConfig() to tell Bukkit to take the current state of your config and copy it into your config file.

GWT: Capturing URL parameters in GET request

I need to build a GWT application that will be called by an external application with specific URL parameters.
For example:
http://www.somehost.com/com.app.client.Order.html?orderId=99999.
How do I capture the orderId parameter inside the GWT application?
Try,
String value = com.google.gwt.user.client.Window.Location.getParameter("orderId");
// parse the value to int
P.S. GWT can invoke native javascript which means if javascript can do the stuff, GWT can do it too; e.g. in GWT, you can write
public static native void alert(String msg)
/*-{
$wnd.alert("Hey I am javascript");
}-*/;
In this case, you can even use existing javascript lib to extract param's value in the querystring.
GWT has a facility to get params from the URL:
String value = Window.Location.getParameter("param");
Make sure your URLs are in the form of:
http://app.com/?param=value#place instead of http://app.com/#place&param=value
In order to get all params in a map, use:
Map<String, List<String>> map = Window.Location.getParameterMap();
I suggest you to use GWT MVP .
Assume that your url as
http://www.myPageName/myproject.html?#orderId:99999
And in your AppController.java --
Try as
......
public final void onValueChange(final ValueChangeEvent<String> event) {
String token = event.getValue();
if (token != null) {
String[] tokens = History.getToken().split(":");
final String token1 = tokens[0];
final String token2 = tokens.length > 1 ? tokens[1] : "";
if (token1.equals("orderId") && tonken2.length > 0) {
Long orderId = Long.parseLong(token2);
// another your operation
}
}
}
...........
Another option , you can also use with Spring MVC. Here is an example ...
// Below is in your view.java or presenter.java
Window.open(GWT.getHostPageBaseURL() + "customer/order/balance.html?&orderId=99999",
"_self", "enable");
// Below code in in your serverside controller.java
#Controller
#RequestMapping("/customer")
public class ServletController {
#RequestMapping(value = "/order/balance.html", method = RequestMethod.GET)
public void downloadAuctionWonExcel(#RequestParam(value = "orderId", required = true) final String orderId,
final HttpServletResponse res) throws Exception {
try {
System.out.println("Order Id is "+orderId);
// more of your service codes
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
You can use the Activities and Places to do that. When you create the Place for your page, you can set the orderId as a member. This member can be used afterwords when you create the Activity associated with the place (in ActivityMapper).
The only restriction is that you can't send the orderId as a normal parameter. You will have to use an url with this form :
127.0.0.1:60206/XUI.html?#TestPlace:orderId=1

Categories

Resources