It is a common way to decouple text messages and source code in C/Python/PHP/etc by means of gettext set of utilities. I'm trying to do something similar in my Java project, according to this instruction. Is it the best possible way? Or I should try something different and more advanced?
ps. I would like to avoid complex initialization and ideally my Java code should look like:
[...]
public String howBigIsTheFile(File f) {
String name = f.getAbsolutePath();
long length = f.length();
return _("The file %s is %d bytes long", name, length);
}
Something like gettext-commons maybe?
I assume this question is about stand-alone application (command-line, SWING, etc) and not about server-side application (with multiple users accessing concurrently).
In stand-alone application, the easiest is to create a single static accessor class that would be responsible for loading a single resource bundle and then looking up strings in that resource bundle.
Something like this:
public class ResourceUtil {
private static ResourceBundle rb;
static {
//set the default locale
setLocale(Locale.ENGLISH);
}
public static void setLocale(Locale locale) {
rb = ResourceBundle.getBundle("Resources", locale);
}
public static String tr(String key, Object... args) {
return MessageFormat.format(rb.getString(key), args);
}
}
You can change the active locale with setLocale(Locale) method and access translated strings with tr(String,Object...) method.
Then, you could call it from your class like this:
import static ResourceUtil.tr;
public String howBigIsTheFile(File f) {
String name = f.getAbsolutePath();
long length = f.length();
return tr("The file %s is %d bytes long", name, length);
}
Notice the static import.
Disclaimer: all provided code is on pseudo-code level and is not guaranteed to compile.
Depending on the size of your application, you might find it useful to use IDE string externalization support (e.g. see chapter in Eclipse JDT help, I'm sure other IDEs have some similar features).
You could also use several resource bundles and/or several static classes - it depends on the size of your application and your personal preference. See this question for further debate about this.
In a server-environment using static approach like above would lead into issues as different users would have different locales. Depending on your webapp framework, you would solve this issue differently (e.g. use MessageSource in Spring).
If you think about I18N/L10N, Java has its own mechanism here: the properties file. You can see an example in the internationalization tutorials. It's even simpler than gettext stuff :).
Related
This morning I fell into a particular case that never happened to me before. I'm developing a Minecraft plugin using the minecraft server API which is usually called NMS with reference to the name of its packages (eg net.minecraft.server.v1_13_R1 for version 1.13).
The main problem with the use of the minecraft server API is that it is difficult to write a cross version code: indeed the name of the packages changes with each new version.
When the plugin only supports two versions it is usually easier to use the interfaces to write two different codes depending on the version. But when you have to support a dozen different versions (and this is my case), it's a bad idea (the plugin would be much too heavy, it would have to import every jar in the IDE, and I would have to redo the code with each new version).
In these cases I usually use reflection but I do not think it's possible here:
packet = packetConstructor.newInstance(
new MinecraftKey("q", "q") {
#Override
public String toString() {
return "FML|HS";
}
},
packetDataSerializerConstructor.newInstance(Unpooled.wrappedBuffer(data)));
As you probably guessed MinecraftKey is a class from NMS and I was told to use Java Dynamic Proxy API. I have never used it and would like to know if you would know a place that would explain to me how to do it simply? If you know of another better method that interests me too!
When I think about it, I think that this is really a lot of trouble for a tiny piece of code x)
EDIT :
My plugin uses the PacketPlayOutCustomPayload (aka plugin messages) to communicate with the mods of the players. It allows me to send a message (a byte []) on a particular channel (a String). But with the 1.13 this String has been replaced by a MinecraftKey (a wrapper for the String that replaces some characters and requires the use of a ":"). This poses a problem when players connect to 1.12 on my 1.13 server so I do not have a choice: I have to override the MinecraftKey object in this case.
I don’t really think using proxy classes is good solution here, it will only make it harder to debug, but if you need something like that you should use library like ByteBuddy: (as java can’t generate proxy for a class, only interfaces are allowed)
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class Main {
public static void main(String[] args) throws Exception {
SomeKey someKey = new SomeKey("my", "key");
System.out.println(someKey); // :<
// this class should be cached/saved somewhere, do not create new one each time.
Class<? extends SomeKey> loaded = new ByteBuddy()
.subclass(SomeKey.class)
.method(named("toString").and(returns(String.class).and(takesArguments(0))))
.intercept(FixedValue.value("something"))
.make()
.load(Main.class.getClassLoader()).getLoaded();
someKey = loaded.getConstructor(String.class, String.class).newInstance("what", "ever");
System.out.println(someKey); // YeY
}
}
class SomeKey {
final String group;
final String name;
public SomeKey(String group, String name) {
this.group = group;
this.name = name;
}
public String getGroup() { return this.group; }
public String getName() { return this.name; }
#Override public String toString() {
return group+":"+name;
}
}
But I would just create separate modules in my project, one that does only work with real bukkit API and contains many interfaces to represent NMS types in some normalised and readable way.
And separate modules for each version, then you will not have much code to duplicate, as most of it will be abstracted and handled by that “core/base” module.
Then you can build it as one single fat jar or separate .jar per version.
Other solution might be using some template engines and preprocessors to generate java sources on build time, see how fastutil is doing this:
https://github.com/vigna/fastutil
And yet another solution for simple classes and parts of code would be to use build-in javascript or external script language like groovy to also create this pattern-line but in runtime. But I would use this only for simplest stuff.
Also for just using methods you can just use normal reflections.
You can also always inject into netty and instead of using default packet serializer just write own bytes, then you don't need that key at all.
My application uses a number of API keys and URLs that I can't allow users to see. I've been putting them directly in the code, sometimes embedded in the method that uses them and sometimes in a class called MyVals that stores commonly used strings and numbers.
e.g.:
public class MyVals {
private LGVals(){}
public static final String DB_URL = "https://mydb.dbprovider.org/";
public static final String DB_Key = "klJF*(oh8iyhkkde";
public static final String TableKey_UserList = "o9w6owifhayf98fnwihj";
}
Is this safe? Does it make a difference if I'm creating my APK with Proguard? What's the best practice for this?
It is absolutely not safe to embed sensitive strings into your code. Proguard obfuscates the class and method names, but any defined strings remain completely unchanged. Here's an example from an app that I've run through Proguard. You can see that the method and class names are obfuscated, but the strings (as they must be) are unchanged.
Here is the original code:
public void shutdown() {
if (logger.logDebug()) {
logger.logDebug(LOGTAG, "Queuing shutdown message ...");
}
queueMessage(new BaseMessage(true));
}
And the obfuscated code, easily obtained by JD-GUI:
public void c()
{
if (c.logDebug())
c.logDebug("MsgRouter", "Queuing shutdown message ...");
a(new a(true));
}
If you embed sensitive data in a String in your app, it can and will be decompiled.
No, this is not secure.
You should at least put the Information into the SharedPreferences.
But still, anybody with root-access could read them.
I do have some trouble understanding the log4j2 wrapper usage.
If you follow this link you will find attached an example using the AbstractLoggerWrapper. I just copied the following peace of code.
public class Log4j2Logger extends AbstractLogger
{
private static final String FQCN = AbstractLogger.class.getName();
private AbstractLoggerWrapper logImpl;
public Log4j2Logger(String name, String prefix, String logId, String instanceId)
{
super(name, prefix, logId, instanceId);
final AbstractLogger logger = (AbstractLogger) LogManager.getLogger(name);
this.logImpl = new AbstractLoggerWrapper(logger, name);
}
....
#Override
public void log(String message, LogLevel level)
{
logImpl.log(null, FQCN, toImplLevel(level), new SimpleMessage(createMessage(message)), null);
}
....
}
I don't understand the reason for subclassing AbstractLogger and intern using the AbstractLoggerWrapper. I actually could just remove the extend from the Log4j2Logger and encapsulate the AbstractLoggerWrapper. Do you see any reason of doing it like in the code snipped above?
Is there any way to subclass the AbstractLogger (like preferred) and just use it without the wrapper? And create it like a strategy pattern? e.g.,
LogManager.getLogger( class.getName(), Log4j2Logger.class )
Maybe this is what they tried to explain in the extending section and I don't understand it, yet. Somebody any idea how to do it?
Sincerely
Christian
Update: I missed to say, the reason why I am using the wrapper is because of an existing projekt with log4j (1.2) with a wrapper.
If you look at the documentation for AbstractLoggerWrapper:
Wrapper class that exposes the protected AbstractLogger methods to
support wrapped loggers.
You will see a clear indication as to why it is done the way Apache does it. If you instead decide to ignore the contract of the interface and go your own way, you are essentially saying
"I don't care how the library does things, I know better"
As such, you are taking on a great deal of risk, instead of using the general solution that has been provided. I sincerely doubt that you have such an esoteric environment that the library could would be insufficient.
To recap, follow the contract laid out by the library documentation.
Programming in Java, what's the better way to create resource files to save the messages of the application in different languages?
If I use a properties file, inside the code everytime that I need refer to a message I have to write something like that:
ResourceBundle.getBundle("test.messages.messageFile").getString("Message1")
Exists another form to use messages and generate a code cleaner? Maybe create a class with static constants that refer to this messages?
you can use ResourceBundle, but declare it and use it as a variable.
ResourceBundle languageBundle=ResourceBundle.getbundle("test.messages.messageFile");
//later in your code you can say
languageBundle.getString("Message1");
it would help if you make this variable public static
This is the standard way. But nothing forces you to not store the bundle in a field:
private ResourceBundle messages = ResourceBundle.getBundle("test.messages.messageFile");
...
String message1 = messages.getString("Message1");
You may also encapsulate it into a Messages class that has the following method:
public String getMessage1() {
return messages.getString("Message1");
}
It's all up to you.
you can try Internationalization made easier.
This is based on annotations and gives type safety for argument substitution.
i was wondering if it's possible to initialize a constant in an interface from a property file using java or using spring messageSource, or such thing is not possible
please advise, thanks.
You can:
public interface Foo {
String a = Properties.getProperty("foo"); // public static final by default
}
However, that means that Properties.getProperty(..) has to be a static method which relies on an already initialized message source (statically again). Depending on the project and the frameworks you use this might not be the best option.
You could initialise a bean via a configuration which includes a final member. Since it's final you can assign to it during construction/initialisation and it then is immutable.
To configure from a property file using Spring, check out the PropertyPlaceholderConfigurer. That will allow you to initialise Spring beans using one or more property files from your classpath, filesystem, remote services etc.
Yes, that's possible:
public static final CONSTANT = System.getProperty("myProperty");
Although it's possible using some static helper method (as was already suggested), I would strongly recommend you not to do so for 2 reasons:
That looks like a pretty bad design. If you need a dynamic value - make it a method in the interface. Or use a static helper directly - you will need one anyway to make it work.
Constants might be inlined at compile time. That shouldn't happen in this particular case - compiler should go with inlining only if it can prove that value won't change between executions, basically if you initialize it with a literal, But there is a tiny chance that it would. Just think how bad will it be - no matter in which environment the progran is running, it picks up some useless value set during compilation, instead of what is configured. (This is rather a theoretical problem, need to say).
by reading a property file like in the example below.
int property1;
String otherProperty;
public void loadProperties(File propFile) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(propFile));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("Property1=")) {
property1 = Integer.parseInt(line.substring(10));
}
if (line.startsWith("OtherProperty=")) {
otherProperty = line.substring(14);
}
}
}