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

有帮助吗?

解决方案 4

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);
        }
    }

}

其他提示

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:

<a href="<c:url value="/referrals/send.html"/>" target="_blank">Refer an Entrepreneur!</a>

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top