I am developing a graphical installer for our application. Since none of the available installer generators meet the requirements and constraints, I am building it from scratch.
The installer is supposed to run on several operating systems, and therefore the path handling needs to be OS-agnostic. I have written the following small utility for this purpose:
public class Path {
private Path() {
}
public static String join(String... pathElements) {
return ListEnhancer.wrap(Arrays.asList(pathElements)).
mkString(File.separator);
}
public static String concatOsSpecific(String path, String element) {
return path + File.separator + element;
}
public static String concatOsAgnostic(String path, String element) {
return path + "/" + element;
}
public static String makeOsAgnostic(String path) {
return path.replace(File.separator, "/");
}
public static String makeOsSpecific(String path) {
return new File(path).getAbsolutePath();
}
public static String fileName(String path) {
return new File(path).getName();
}
}
Now my code is littered with Path.*Agnostic and Path.*Specific calls in many places. As is apparent, this is very error-prone and not transparent at all.
What approach should I take to make the path handling transparent and less error-prone? Do there exist any utilities/libraries that already address this problem? Any help would be greatly appreciated.
EDIT:
To exemplify what I mean, here is some code I wrote a while ago. (Offtopic: Forgive the long-ish method. The code is in initial stages, and will be undergoing some heavy refactoring soon.)
Some context: ApplicationContext is an object that stores the installation data. That includes several paths such as installationRootDirectory, installationDirectory etc. The defaults for these are specified when creating an installer, and hence are always stored in OS-agnostic formats.
#Override
protected void initializeComponents() {
super.initializeComponents();
choosePathLabel = new JLabel("Please select the installation path:");
final ApplicationContext c = installer.getAppContext();
pathTextField = new JTextField(
Path.makeOsSpecific(c.getInstallationDirectory()));
browseButton = new JButton("Browse",
new ImageIcon("resources/images/browse.png"));
browseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
int choice = fileChooser.showOpenDialog(installer);
String selectedInstallationRootDir = fileChooser.getSelectedFile().
getPath();
if (choice == JFileChooser.APPROVE_OPTION) {
c.setInstallationRootDirectory(
Path.makeOsAgnostic(selectedInstallationRootDir));
pathTextField.setText(Path.makeOsSpecific(c.getInstallationDirectory()));
}
}
});
}
Or you could introduce 2 new classes:
class OsSpecificPath implements FilePathInterface
{
String path;
OsAgnosticPath toAgnosticPath();
OsSpecificPath concat( OsSpecificPath otherPath );
// from IFilePath
getFile();
... etc
}
and
class OsAgnosticPath implements FilePathInterface
{
String path;
OsSpecificPath toOsSpecificPath();
OsAgnosticPath concat( OsAgnosticPath otherPath );
// from IFilePath
getFile();
... etc
}
each wrap a path however they need to.
each method could then have methods to convert to the other type of path, but instead of a "stringly-typed" solution where everything is a string and can be misused, you'd have 2 strongly typed classes that can't be incorrectly passed around.
Anything that doesn't care about the type of path would use FilePathInterface, anything that needs to operate on specific kinds of paths would use those types specificly. FilePathInterface could hypothetically have both toAgnosticPath and toOsSpecificPath in the interface if really necessary...
Not sure if this is what you're going for, but usually when I need to do something path-related in an OS-independent Java program, I always use Strings to pass paths around instead of Files, and I always do the following two things:
Whenever I am building a String path, I always use / as the file separator
Whenever I use a String path to create a File or save it as text somewhere, I always make the following calls prior to using the path:
String fSep = System.getProperty("file.separator);
String path = ... //might be built from scratch, might be passed in from somewhere
path = path.replace("/",fSep).replace("\\",fSep);
This seems to work well regardless of whether the path gets built on the local machine or if it gets passed in from a different machine on the network with a different OS, provided that I intend to use the path on the local machine. If you plan to pass the path between different OS'es via networking, just be careful that your own code is consistent.
EDIT
Wow... somehow my answer got mangled up and the code formatting didn't work as initially intended...
You never need to convert back to os-agnostic. here are the conversions to os-specific:
public class Path {
private Path() {
}
public static String concat(String path, String element) {
return new File(path, element).getPath();
}
public static String makeOsSpecific(String path) {
return new File(path).getAbsolutePath();
}
public static String fileName(String path) {
return new File(path).getName();
}
}
Your sample:
#Override
protected void initializeComponents() {
super.initializeComponents();
choosePathLabel = new JLabel("Please select the installation path:");
final ApplicationContext c = installer.getAppContext();
pathTextField = new JTextField(
Path.makeOsSpecific(c.getInstallationDirectory()));
browseButton = new JButton("Browse",
new ImageIcon("resources/images/browse.png"));
browseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
int choice = fileChooser.showOpenDialog(installer);
String selectedInstallationRootDir = fileChooser.getSelectedFile().
getPath();
if (choice == JFileChooser.APPROVE_OPTION) {
c.setInstallationRootDirectory(selectedInstallationRootDir);
pathTextField.setText(Path.makeOsSpecific(c.getInstallationDirectory()));
}
}
});
}
I would make my own MyFile object that extends or wraps java.util.File. Then make sure all your code uses this object instead of java.io.File. In here you would be doing the OS checks and calling methods to clean up the file name. The rest of your code would be 'clean'.
Related
I am really needing some help on this.
I have adopted the JNOTIFY approach to detecting any new files in a directory. When the file arrives the Listener informs that a new file is in the location.
#BeforeTest(alwaysRun=true)
public void Polling() throws Exception {
ListenToNotifications.checkFolderPickup();
}
I have attempted this where I addded a call to my Setup function in order to call my setup function after the file is detected.
//snippet from Listener Class from checkFolderPickup();
public void fileCreated(int wd, String rootPath, String name) {
print("New File just created " + rootPath + " : " + name);
Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
try {
BaseTest.setup();
} catch (Exception e) {
e.printStackTrace();
}
}
My question is //Thread.sleep(1000000) i feel this is not a safe approach and I wanted to know if there is any other approach that I could possibly use instead of a Thread.Sleep, because this function will have to be executed once each time a new file is available and the old file will be deleted eventually and so on, I cannot make the Sleep to short , it will just ignore and continue with Base.Setup()
public static void checkFolderPickup() throws Exception {
...removed other code
boolean watchSubtree = true;
int watchID = JNotify.addWatch(path, mask, watchSubtree, new Listener());
//Thread.sleep(1000000);
Thread.sleep(20000);
boolean res = JNotify.removeWatch(watchID);
if (!res) {
// invalid watch ID specified.
}
}
I basically need my framework to keep polling that directory and each time it will execute the base setup process and follow a workflow, delete the file then poll again and so on.
Can anyone please advise?
You don't need any other modules , you can use custom expected condition:##
using:
import java.io.File;
define the method inside any pageobject class:
private ExpectedCondition<Boolean> newfilepresent() {
return new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(WebDriver driver) {
File f = new File("D:\\workplace");
return f.listFiles().length>1;
}
#Override
public String toString() {
return String.format("wait for new file to be present within the time specified");
}
};
}
we created a custom expected condition method now use it as:
and in code wait like:
wait.until(pageobject.filepresent());
Output:
Failed:
Passed
Once you register a watch on a directory with JNotify, it will continue to deliver events for files in that directory. You should not remove the watch if you wish to continue to receive events for that directory.
In the following Java program (only showing a part of the whole program)
I have the follwing code:
private final File test_data_file;
private final int algorithm_selection;
private double average_pos_err, average_exe_time;
// The scan list to use for offline
private ArrayList<LogRecord> OfflineScanList;
public OfflineMode(RadioMap RM, File test_data_file, int algorithm_selection, Handler handler) {
this.RM = RM;
this.test_data_file = test_data_file;
this.handler = handler;
this.algorithm_selection = algorithm_selection;
this.OfflineScanList = new ArrayList<LogRecord>();
}
public void run() {
if (!test_data_file.isFile() || !test_data_file.exists() || !test_data_file.canRead()) {
errMsg = test_data_file + " does not exist or is not readable";
handler.sendEmptyMessage(-1);
return;
}
I want to track down, the path of the variable "test_data_file" of type File, but the code does not seems to show me any directions.
do you know where I can find it?
Thank you.
You can use File#getAbsolutePath or File#getCanonicalPath which will resolve the path fully (removing relative components)
using by java 7 you can get file type
String fileType = Files.probeContentType(test_data_file.getPath());
for more detail you can refers
http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#probeContentType%28java.nio.file.Path%29
I'm programming a simple sketch in processing (a type of java), and using this bit of code to scan a folder for jpgs. I would like to be able to use it as a function, so I can use it like this:
String[] images;
void setup() {
images = scanForJpgs();
}
String[] scanForJpgs() {
// set target folder
java.io.File folder = new java.io.File(dataPath(""));
// set filter (which returns true if file's extension is .jpg)
java.io.FilenameFilter jpgFilter = new java.io.FilenameFilter() {
boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jpg");
}
};
// list files in target folder, passing the filter as parameter
String[] filenames = folder.list(jpgFilter);
return filenames;
}
But it throws a "Cannot reduce the visibility of the inherited method from FilenameFilter" error. How do I fix this so I can use it like a function?
Declare the method public rather than package-private:
public boolean accept(File dir, String name) {
Read: Controlling Access to Members of a Class
I'm currently developing a web site using servlets & spring framework. As usual it contains lots of files (jsp, js, css, images, various resources etc).
I'm trying to avoid writing any hardcoded path, or domain in any file ...
For example as you may know when a request is handled you 'forward' it to a jsp page (it's path probably will be hardcoded). Other examples are imports images/css/js etc in jsp files ...
Is there any general way (or tools) to avoid hardcoded paths/urls so any refactorings won't cause troubles?
EDIT
I use netbeans 7.1.2 ... Unfortunately netbeans only helps with pure java code. When working with jsp files things are limited, and if you add custom tag files and Jsp 2.0 EL is like programming in console mode :p
In the JSP files themselves, you can avoid nearly all hardcoded domain / urls by using JSTL
For example, when creating a link to another page, you would do it like this:
Refer an Entrepreneur!
This means that, regardless of where your webapp is, the link will always have the right url. For example, in my development box this link would be:
http://localhost:8080/accounts/referrals/send.html
But on my production server, it resolves correctly to:
http://wwww.mydomain.com/referrals/send.html
You can see that in my dev server, the webapp context is under /accounts, but on the production machine, it's just under / as the webapp is under the root context.
You can read a small tutorial here
Properties file is always a good option so that you have to make changes if any only at one point.
If you are referencing any static contents (js, images, css, etc), you don't have to hardcode the entire file path. Instead, you can do this:-
<img src="${pageContext.request.contextPath}/resources/images/test.jpg"/>
The rest of the file paths (Hibernate domain mappings, forwarded page in Spring controller, etc) should be relative to your project structure, and most IDEs are smart enough to refactor them without problem... or at least in my case, IntelliJ seems to handle of all that for me.
At some point of time, you need to ask yourself, how much of hardcoding is acceptable vs not acceptable? Further, I wouldn't try to stray too far away from the Spring/Hibernate recommended solutions. If you make everything too abstract, you have a different set of problem to deal with and it becomes counterproductive to other peers that may be inheriting your project in the future.
Actually I just came up with an idea. Since netbeans does analysis and shows dependencies on java code, maybe it's better to handle all paths & domains as java variables.
I've created a package on my project named FileResolver and inside I have one class for each file type on my project (eg one class for Jsp files, one for Css files etc). Inside those files I'll record & hardcode all paths of all files in public static final String variables. Sample:
public class Jsps {
public class layouts{
public static final String main = "layouts/main.jsp";
}
public class pages{
public static final String error = "pages/error.jsp";
public static final String login = "pages/login.jsp";
public static final String register = "pages/register.jsp";
}
...
}
All over my project I should use the variables instead of paths. Then anytime I refactor a file, I'll have only one file to change is the mapping value in those variables ...
And if somethime I need to change the variable, netbeans will refactor all of them in the project at once ...
I think this will work just fine since I keep my project clean from file paths and the only thing I have to worry about is the mapping in that file of the variables to appropriate file paths.
EDIT
I'll write a simple parser to create those java files instead of writting by hand for all files ... I'll update when I finish it
UPDATE
Here is my FileResolverGenerator
public class FileResolverGenerator {
private static final String newFilePath = "C:/Users/Foo/Desktop/Jsps.java";
private static final String scanRootFolder = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp";
private static final String varValueReplaceSource = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp/";
private static final String varValueReplaceTarget = "";
private static final boolean valueAlign = true;
private static final int varNameSpaces = 15;
public static void main(String[] args){
try {
// Create file and a writer
File f = new File(newFilePath);
f.createNewFile();
bw = new BufferedWriter( new FileWriter(f) );
// Execute
filesParser( new File(scanRootFolder) );
// 'Burn' file
bw.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(ResolverGenerator.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(ResolverGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
}
// ================================================================================================ //
// ============================================= WORK ============================================= //
// ================================================================================================ //
private static void filesParser(File rootFolder) throws FileNotFoundException, IOException{
folderIn(rootFolder);
// Files first
if(!rootFolder.exists()) throw new FileNotFoundException();
for(File f : rootFolder.listFiles()){
if(f==null){ return; }
if(f.isDirectory()){ continue; }
else if(f.isFile()){ writeFileVariable(f); }
}
// Folders next
for(File f : rootFolder.listFiles()){
if(f==null){ return; }
if(f.isDirectory()){ filesParser(f); }
else if(f.isFile()){ continue; }
}
folderOut(rootFolder);
}
// ================================================================================================ //
// ============================================ PRINTS ============================================ //
// ================================================================================================ //
private static BufferedWriter bw;
private static int tabCount = 0;
private static void folderIn(File f) throws IOException{
bw.append("\n\n");
for(int i=0; i<tabCount; i++)
bw.append("\t");
bw.append("public class "+f.getName()+"{\n");
tabCount++;
}
private static void folderOut(File f) throws IOException{
tabCount--;
for(int i=0; i<tabCount; i++)
bw.append("\t");
bw.append("}\n");
}
private static void writeFileVariable(File f) throws IOException{
String varName = f.getName().split("\\.")[0].replaceAll("-", "");
String varValue = f.getPath().replaceAll("\\\\","/")
.replace(varValueReplaceSource.replaceAll("\\\\","/"),varValueReplaceTarget.replaceAll("\\\\","/"));
for(int i=0; i<tabCount; i++)
bw.append("\t");
bw.append("public static final String "+varName+" = ");
if(valueAlign){
for(int i=0; i<varNameSpaces-varName.length(); i++) bw.append(" ");
bw.append("\t"); }
bw.append("\""+varValue+"\";\n");
}
}
Just to be specific ... This scans all files under "/WEB-INF/jsp/" and creates a java file having all jsp files 'registered' to public static final String variables with each path ... The idea is to use the generated java file as reference for all jsps are in project ... always use these variables instead of hardcoded paths ..
This has nothing to do with the project or any project. It's just a tool which saves you
time, instead of doing this by hand for every file in the project.
I also created another class ResolverConsistencyChecker, which takes all variables and checks if the filepath is correct (file exists) ... since we didn't made any changes to filenames and filepaths all tests are passed.
This method should run when testing project for 'errors'
public class ResolverConsistencyChecker {
private static Class checkClass = Jsps.class;
private static String fullPathPrefix = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp/";
public static void main(String[] args){
try {
filesChecker( checkClass );
System.out.println( "Tests passed. All files locations are valid" );
} catch (FileNotFoundException ex) {
Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
}
}
// ================================================================================================ //
// ============================================= WORK ============================================= //
// ================================================================================================ //
private static void filesChecker(Class rootClass) throws FileNotFoundException, IOException{
// Check file paths in current class depth
for(Field f : rootClass.getFields()){
try {
String fullFilePath = fullPathPrefix+f.get(f.getName()).toString();
File file = new File( fullFilePath );
if( !file.exists() )
throw new FileNotFoundException("Variable: '"+f.getName()+"'\nFile "+fullFilePath);
} catch (IllegalArgumentException ex) {
Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Check for embedded classes
for(Class c : rootClass.getClasses()){
filesChecker(c);
}
}
}
I was wondering if there is a better way to have a point to PATH in a properties file. Consider the following code:
public class Properties
{
//MIKE
public final static String PATH_TO_FILE_A = "C:\\programmer_MIKE\fileA.txt";
public final static String PATH_TO_FILE_B = "C:\\programmer_MIKE\fileB.txt";
//BILL
//public final static String PATH_TO_FILE_A = "/Users/BILL/Desktop/fileA.txt";
//public final static String PATH_TO_FILE_B = "/Users/BILL/Desktop/fileB.txt";
}
when any developer need to invoke FILE_A he simply does:
File file = new File(Properties.PATH_TO_FILE_A);
this works ok for BILL if he commented out MIKE's PATH_TO_FILE_A.
Q: is there a better design? If BILL committed his work including the Properties file - he will cause a problem to MIKE (no worries, he'll get a Coffee Latte later on).
the FILES are big (2-4Gb) and we don't want to put them in our repository (svn) and sometimes there are simply temporary folder to create a PDF so we don't want to put them in a "./docs" path.
Thanks for any pointer!
If for whatever reason you really must have hardcoded paths, then you could store them in some kind of map indexed by username. Something like:
public class Properties {
private static Map<String, DeveloperPaths> properties = create();
private static Map<String, DeveloperPaths> create() {
Map<String, DeveloperPaths> properties = new HashMap<String, DeveloperPaths>();
properties.put("mike", new DeveloperPaths(
"C:\\programmer_MIKE\fileA.txt",
"C:\\programmer_MIKE\fileB.txt")
);
properties.put("bill", new DeveloperPaths(
"/Users/BILL/Desktop/fileA.txt",
"/Users/BILL/Desktop/fileB.txt")
);
return properties;
}
public static File FileA()
{
String user = System.getProperty("user.name");
return properties.get(user).fileA;
}
public static File FileB()
{
String user = System.getProperty("user.name");
return properties.get(user).fileB;
}
}
class DeveloperPaths {
public File fileA;
public File fileB;
public DeveloperPaths(String pathA, String pathB) {
fileA = new File(pathA);
fileB = new File(pathB);
}
}
Then, the code to access each path would be identical regardless of developer, for example:
File myFile = Properties.fileA();
Normally paths are configurable entites and should be stored in property file.
Property files has a build in support in java and it uses Properties object for storing that information.
You can read the property file at startup or init (or similar) method of your application and read the proeprties from property file. This will make your configuration dynamic and anyone would be able to change it.
You can create a static method and call it on startup like:
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class GetProperties {
public static Properties prop = new Properties();
public static void init() {
InputStream inputStream = GetProperties.class.getClassLoader().getResourceAsStream("application.properties");
try {
prop.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Things like this should be configured externally and/or passed in via parameter, system parameter, or environment variable. Alternatively you could use DI/IoC, but when there's no attached behavior, IMO a config value is plenty.
It's fine to have a hard-coded default, but otherwise stuff like this doesn't belong in code.