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.
Related
I used code of programm that you can see below.The logic of class is to get some properties from html code from YouTube page.For long time it worked fine, but now not. The reason of problem is the next: jdk/jre uses Internet explorer as default browser and now YouTube not support ie (It returns the page with suggestion of updating browser).
The question is : how to change default browser taht java uses?
I switched the default browser of the system to Chrome and default browser of Intellij IDE to Chrome too, but it didn't give any result to me.
#Component(immediate = true, service = LastActualVideoService.class)
public class LastActualVideoServiceServiceImpl implements LastActualVideoService {
private final Logger logger = LoggerFactory.getLogger(getClass());
private static final String LINK_TO_YOU_TUBE = "https://www.youtube.com/embed/";
private static final String TRIGGER_FOR_VIDEO = "/watch?v=";
private static final String VIDEO_SELECTOR = "/videos";
private static final String HTML_SEPARATOR = "\\A";
private static final String ERROR_MASSAGE = "Incorrect input URL";
private static final String OPEN_TITLE_TAG = "<title>";
private static final String CLOSE_TITLE_TAG = "</title>";
#Override
public YouTubeChannelInfo getVideoBlob(String channelURL) {
channelURL = channelURL.concat(VIDEO_SELECTOR);
try (InputStream response = new URL(channelURL).openStream()) {
Scanner scanner = new Scanner(response);
String responseBody = scanner.useDelimiter(HTML_SEPARATOR).next();
String uniqueVideo = responseBody.substring(responseBody.indexOf(TRIGGER_FOR_VIDEO), responseBody.indexOf(TRIGGER_FOR_VIDEO) + 20);
String title = responseBody.substring(responseBody.indexOf(OPEN_TITLE_TAG) + 7, responseBody.indexOf(CLOSE_TITLE_TAG));
String linkToVideo = LINK_TO_YOU_TUBE.concat(uniqueVideo.substring(uniqueVideo.lastIndexOf('=') + 1));
return new YouTubeChannelInfo(linkToVideo, title, channelURL);
} catch (IOException e) {
logger.error(ERROR_MASSAGE, e);
return null;
}
}
}
URL.openStream does not "use the browser", your Java program acts as HTTP client itself. The way the remote server can know what type of browser is connecting is the user agent that the client sends with the request. It's possible that Youtube does not recognize or like whatever the default is.
Like Joachim Rohde commented, the solution is to manually set the user agent to something Youtube will recognize as supported.
I have a project, where I am given an id, and then using that ID look up files paths and process them... these files are on various mounted drives, so I am using the SMBJ java libraries to access them.
The problem I am having is that some (most) of the files are using a DFS mountpoint... Now, this in and of itself is NOT a problem per se, but apparently the SMBJ libraries appear to create nested sessions for each distinct DFS location. So even though I am closing the actual FILE after I am done reading it the DiskSession object is holding onto all these nested sessions... and eventually either through the DFS config settings, or through these libraries I am hitting some point where it just blows up and stops allowing more sessions to be created.
I am processing hundreds of thousands of records, and the "crash" appears to happen somewhere around 500ish records(session) being processed. I do not see anything obvious looking at the code to explicitly close these nested sessions.. in fact I see no external access to them at all externally from the DiskShare object.
Is there some sort of setting I am missing that maximizes the sessions that this is holding onto? Other than me managing some sort of my own counter around this, and closing and reopening sessions/connections I am at a loss how to handle this.
Does anyone know what I am missing here?
Code below:
public class Smb {
private static SMBClient client;
private static String[] DFSMounts = {"DFS1","dfs1"};
private static final Logger Log = LoggerFactory.getLogger(Smb.class);
private static HashMap<String,DiskShare> shares = new HashMap<>();
private static HashMap<String,Connection> connections = new HashMap<>();
private static HashMap<Connection,Session> sessions = new HashMap<>();
private synchronized static SMBClient getClient(){
if (client == null){
SmbConfig cfg = SmbConfig.builder().withDfsEnabled(true).build();
client = new SMBClient(cfg);
}
return client;
}
private synchronized static Connection getConnection(String realDomainName) throws IOException{
Log.info("DOMAIN NAME "+realDomainName);
Connection connection = (connections.get(realDomainName) == null) ? client.connect(realDomainName) : connections.get(realDomainName);
if(!connection.isConnected()) {
connection.close();
sessions.remove(connection);
connection = client.connect(realDomainName);
}
// connection = client.connect(realDomainName);
connections.put(realDomainName,connection);
return connection;
}
private synchronized static Session getSession(Connection connection,SMBClient client){
Session session = sessions.get(connection);
if(session==null) {
PropertiesCache props = PropertiesCache.getInstance();
String sambaUsername = props.getProperty("smb.user");
String sambaPass = props.getProperty("smb.password");
String sambaDomain = props.getProperty("smb.domain");
Log.info("CLIENT " + client);
session = (sessions.get(connection) != null) ? sessions.get(connection) : connection.authenticate(new AuthenticationContext(sambaUsername, sambaPass.toCharArray(), sambaDomain));
sessions.put(connection, session);
}
return session;
}
#SuppressWarnings("UnusedReturnValue")
public synchronized static DiskShare getShare(String domainName, String shareName) throws SmbException
{
DiskShare share = shares.get(domainName+"/"+shareName);
if((share!=null)&&(!share.isConnected())) share=null;
if(share == null){
try {
PropertiesCache props = PropertiesCache.getInstance();
String sambaUsername = props.getProperty("smb.user");
String sambaPass = props.getProperty("smb.password");
String sambaDomain = props.getProperty("smb.domain");
String dfsIP = props.getProperty("smb.sambaIP");
SMBClient client = getClient();
String realDomainName = (Arrays.stream(DFSMounts).anyMatch(domainName::equals)) ? dfsIP: domainName;
Connection connection = getConnection(realDomainName);
Session session = getSession(connection,client);
share = (DiskShare) session.connectShare(shareName);
shares.put(domainName+"/"+shareName,share);
}
catch (Exception e){
Log.info("EXCEPTION E "+e);
Log.info("EX "+e.getMessage());
throw new SmbException();
}
}
return(share);
}
public static String fixFilename(String filename){
String[] parts = filename.split("\\\\");
ArrayList<String> partsList = new ArrayList<>(Arrays.asList(parts));
partsList.remove(0);
partsList.remove(0);
partsList.remove(0);
partsList.remove(0);
return String.join("/",partsList);
}
public static File open(String filename) throws SmbException {
String[] parts = filename.split("\\\\");
String domainName = parts[2];
String shareName = parts[3];
DiskShare share = getShare(domainName,shareName);
Set<SMB2ShareAccess> s = new HashSet<>();
s.add(SMB2ShareAccess.ALL.iterator().next());
filename = fixFilename(filename);
return(share.openFile(filename, EnumSet.of(AccessMask.GENERIC_READ), null, s, SMB2CreateDisposition.FILE_OPEN, null));
}
}
And here is how the OPEN is being used (to show it is closing the file after use):
String filename = documents.get(0).getUNCPath();
try (File f = Smb.open(filename)){
Process the file code...
f.closeSilently();
}
And:
while(i.hasNext()){
String filename = (String)i.next();
Log.info("FILENAME "+filename);
try(File f = Smb.open(filename)){
Process the file stuff here
}
}
I have created a PR for SMBJ which changes this. It will reuse the nested session for same host. I have successfully used it myself to avoid the exact same problem you are having. https://github.com/hierynomus/smbj/pull/489
What is the best way of writing a unit test for a method, such as my setProperties (see below), that uses a private configuration variable (config). I tried but failed to override it using reflection and Makito, but without success. I realize that changing the design to make the code easier to test is best, but I want to created some unit tests before I refactor the code.
public class MainClass {
private final java.lang.String config = "app.properties";
public TestClass() {
try {
setProperties();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setProperties() throws Exception {
try {
InputStream input = new BufferedInputStream(new FileInputStream(config));
..
..
} catch (Exception exception) {
throw exception;
}
}
}
Do refactor a tiny bit by extracting a method with a parameter that takes an input stream. Call this new method (probably package-protected) from the old one. Write tests against the new method. Then do more refactorings.
This is an indication of a broken design; don't hard-code things like this. Better yet, determine what the appropriate responsibility for this class is, and, in decreasing order of preference:
pass in an object with the configuration properties, strongly typed
pass in a Map with the configuration properties
pass in an InputStream for the properties file
As File objects are never available from a jar, you shouldn't ever make interfaces like this more specific than InputStream or Reader, so that you can always pass in streams from your jar classpath.
So you can use Properties class in Java for this. Please have a look at this code.
public class PropertyUtil {
private static Properties prop;
private static Logger logger = Logger.getLogger(PropertyUtil.class);
private PropertyUtil() {
}
public void setProperty() {
String filePath = System.getenv("JAVA_HOME") + "/lib" + "/my_file.properties";
prop = new Properties();
try (InputStream input = new FileInputStream(filePath)) {
prop.load(input);
} catch (IOException ex) {
logger.error("Error while reading property file " + ex);
}
}
public static String getProperty(String key) {
if (prop.containsKey(key)) {
return prop.getProperty(key);
} else {
return null;
}
}
public static <T> T getProperty(String key, Class<T> claz) {
if (claz.getName().equals(Integer.class.getName())) {
return claz.cast(Integer.parseInt(prop.getProperty(key)));
}
if (claz.getName().equals(Long.class.getName())) {
return claz.cast(Long.parseLong(prop.getProperty(key)));
}
if (claz.getName().equals(Boolean.class.getName())) {
return claz.cast(Boolean.parseBoolean(prop.getProperty(key)));
}
if (claz.getName().equals(Double.class.getName())) {
return claz.cast(Double.parseDouble(prop.getProperty(key)));
}
if (claz.getName().equals(String.class.getName())) {
return claz.cast(prop.getProperty(key));
}
return null;
}
Below I have a code snippet for a custom API manager mediator, I'm suppose to modify this code for our use. I'm having trouble though getting the logs out of the code when I'm running it in our wso2 environment. What would be the process to be able to the outputs of these logs. This is going to be a jar file I add to the repository/components/lib/ directory of the APIM. The jar file name is com.domain.wso2.apim.extensions. I need to be able to see whats being passed and what parts of the code are being hit for testing
public class IdentifiersLookup extends AbstractMediator implements ManagedLifecycle {
private static Log log = LogFactory.getLog(IdentifiersLookup.class);
private String propertyPrefix = "";
private String netIdPropertyToUse = "";
private DataSource ds = null;
private String DsName = null;
public void init(SynapseEnvironment synapseEnvironment) {
if (log.isInfoEnabled()) {
log.info("Initializing IdentifiersLookup Mediator");
}
if (log.isDebugEnabled())
log.debug("IdentifiersLookup: looking up datasource" + DsName);
try {
this.ds = (DataSource) new InitialContext().lookup(DsName);
} catch (NamingException e) {
e.printStackTrace();
}
if (log.isDebugEnabled())
log.debug("IdentifiersLookup: acquired datasource");
}
Add the below line to log4j.properties file resides wso2am-2.0.0/repository/conf/ folder and restart the server.
log4j.logger.com.domain.wso2.apim.extensions=INFO
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!");
}
}