0

I'm programming an application which uses translation strings (.properties files); those strings are also made of non-latin characters (actually, they are copypastas like lenny smiles and stuff like this). My problem is, when I launch it inside my IDE Netbeans clicking on "clean and build" and then "run", everything goes fine and all the strings are displayed properly... the program acts as intended; but when I export my project as a fat jar (I use Maven shade plugin which also includes my properties files) and when I launch it double-clicking on its icon (still being on Windows) everything is messed up as some strings are not displayed:

inside my program, I use the class LocalisationService (code follows) to load strings inside different ArrayList. Running my project using Netbeans, strings are all loaded correctly (that's the supposed behaviour). Running my jar outside Netbeans, only 1/5 are loaded properly. I have a huge number of "String not found" entries in my arraylists and this means that, inside my LocalisationService class, method getString catches MissingResourceException exception. But actually those resources aren't missing, I mean, I include them properly in my Jar and Netbeans runs my project straight as it is supposed to do, so...

I have totally no idea on what could cause this issue: my IDE project encoding is set on UTF-8 so there shouldn't be any problems... Maven runs my project using:

cd C:\Users\utente\Documents\NetBeansProjects\mavenproject1; "JAVA_HOME=C:\\Program Files\\Java\\jdk1.8.0_40" cmd /c "\"\"C:\\Program Files\\NetBeans 8.0.2\\java\\maven\\bin\\mvn.bat\" -Dexec.args=\"-classpath %classpath bot.Main\" -Dexec.executable=\"C:\\Program Files\\Java\\jdk1.8.0_40\\bin\\java.exe\" -Dmaven.ext.class.path=\"C:\\Program Files\\NetBeans 8.0.2\\java\\maven-nblib\\netbeans-eventspy.jar\" -Dfile.encoding=UTF-8 org.codehaus.mojo:exec-maven-plugin:1.2.1:exec\""

I have the same problem on Ubuntu, strings are messed up launching my file from terminal with

/usr/bin/java -jar myproject.jar

Here is the class LocalisationService I use to retrieve the correct localisation string using a key:

public class LocalisationService {
    private static LocalisationService instance = null;
    private final HashMap<String, String> supportedLanguages = new HashMap<>();

    private ResourceBundle english;
    private ResourceBundle italian;

private class CustomClassLoader extends ClassLoader {
    public CustomClassLoader(ClassLoader parent) {
        super(parent);

    }

    public InputStream getResourceAsStream(String name) {
        InputStream utf8in = getParent().getResourceAsStream(name);
        if (utf8in != null) {
            try {
                byte[] utf8Bytes = new byte[utf8in.available()];
                utf8in.read(utf8Bytes, 0, utf8Bytes.length);
                byte[] iso8859Bytes = new String(utf8Bytes, "UTF-8").getBytes("ISO-8859-1");
                return new ByteArrayInputStream(iso8859Bytes);
            } catch (IOException e) {
                e.printStackTrace();

            } finally {
                try {
                    utf8in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

/**
 * Singleton
 * @return Instance of localisation service
 */
public static LocalisationService getInstance() {
    if (instance == null) {
        synchronized (LocalisationService.class) {
            if (instance == null) {
                instance = new LocalisationService();
            }
        }
    }
    return instance;
}

/**
 * Private constructor due to singleton
 */
private LocalisationService() {
    CustomClassLoader loader = new CustomClassLoader(Thread.currentThread().getContextClassLoader());
    english = ResourceBundle.getBundle("localisation.strings", new Locale("en", "US"), loader);
    supportedLanguages.put("en", "English");
    italian = ResourceBundle.getBundle("localisation.strings", new Locale("it", "IT"), loader);
    supportedLanguages.put("it", "Italiano");
}

/**
 * Get a string in default language (en)
 * @param key key of the resource to fetch
 * @return fetched string or error message otherwise
 */
public String getString(String key) {
    String result;
    try {
        result = english.getString(key);
    } catch (MissingResourceException e) {
        System.out.println("not found key...  "+key);
        result = "String not found";
    }

    return result;
}

/**
 * Get a string in default language
 * @param key key of the resource to fetch from localisations
 * @param language code key for language (such as "EN" for english)
 * @return fetched string or error message otherwise
 */
public String getString(String key, String language) {
    String result;
    try {
        switch (language.toLowerCase()) {
            case "en":
                result = english.getString(key);
                break;
            case "it":
                result = italian.getString(key);
                break;
            default:
                result = english.getString(key);
                break;
        }
    } catch (MissingResourceException e) {
        result = english.getString(key);
    }

    return result;
}

public HashMap<String, String> getSupportedLanguages() {
    return supportedLanguages;
}

public String getLanguageCodeByName(String language) {
    return supportedLanguages.entrySet().stream().filter(x -> x.getValue().equals(language)).findFirst().get().getKey();
}
}

My project has no errors at all, neither warnings... I also tried running my jar file using, on Ubuntu:

/usr/bin/java -Dfile.encoding=UTF-8 -jar myproject.jar

but still with no luck.

I really hope you guys may help me, I'm stuck on this issue since 2 days with no solutions at all...

A7X
  • 119
  • 2
  • 12
  • What does "strings are messed" mean? What happens when you run your program from Netbeans and from the command line? What is the difference? What do you want it to be instead? – Code-Apprentice Nov 29 '16 at 00:49
  • I don't think it's fair to blame the person who asked the question for you not knowing the answer. It seems pretty clear to me from the code what the problem is, but I also have more experience. – Ralph Ritoch Nov 29 '16 at 01:02

1 Answers1

2

Do not expect InputStream.available() to provide you with accurate information.

See the correct way to convert an InputStream to a ByteArrayInputStream here > Convert InputStream(Image) to ByteArrayInputStream

It seems clear that the InputStream provided by the Parent Classloader is somehow different when loaded in Netbeans than when run from your command line JVM implementation. Your code doesn't show the complete context that this code is executed from but it is possible that the InputStream implementation by Netbeans fully populated the available() method giving you the false impression that the code was correct.

See the documentation at https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#available()

InputStream implementations are not required to populate this method with an accurate value so the results of your code will vary by JVM implementation.

Community
  • 1
  • 1
Ralph Ritoch
  • 3,260
  • 27
  • 37