001    package org.springframework.richclient.jnlp;
002    
003    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
004    
005    import javax.jnlp.BasicService;
006    import javax.jnlp.ServiceManager;
007    import javax.jnlp.UnavailableServiceException;
008    import java.util.Properties;
009    
010    /**
011     * Subclass of PropertyPlaceholderConfigurer that resolves the following properties as placeholders:
012     * <ul>
013     * <li><code>jnlp.webAppContextUrl</code> the webAppContextUrl, for example
014     * http://domain.com/petclinic/</li>
015     * </ul>
016     * <p/>
017     * <p>Can be combined with "locations" and/or "properties" values.
018     * <p/>
019     * <p>If a placeholder could not be resolved against the provided local
020     * properties within the application, this configurer will fall back to
021     * the JNLP properties. Can also be configured to let jnlp properties
022     * override local properties (contextOverride=true).
023     * <p/>
024     * <p>If not running within a JNLP context (or any other context that
025     * is able to satisfy the BasicService.lookup call), this class will
026     * use the fallBackWebAppContextUrl. This allows for keeping
027     * JnlpPropertyPlaceholderConfigurer definitions in test suites.
028     * <p/>
029     * <p> A typical usage would be:
030     * <pre>
031     * &lt;bean id="jnlpPropertyPlaceholderConfigurer"
032     *      class="be.kahosl.thot.swingui.util.JnlpPropertyPlaceholderConfigurer"&gt;
033     *      &lt;property name="fallBackWebAppContextUrl" value="http://localhost:8080/mywebapp/"/&gt;
034     *      &lt;property name="jnlpRelativeDirectoryPathFromWebAppContext" value="/jnlp/"/&gt;
035     *  &lt;/bean&gt;
036     *  </pre>
037     * <p/>
038     * Use this in combination with Sun's JnlpDownloadServlet to not have to hardcode your server URL:
039     * http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/downloadservletguide.html
040     *
041     * @author Geoffrey De Smet
042     * @see #setLocations
043     * @see #setProperties
044     * @see #setSystemPropertiesModeName
045     * @see #setContextOverride
046     * @see javax.jnlp.BasicService#getCodeBase()
047     */
048    public class JnlpPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
049    
050        /**
051         * The placeholder for getWebAppContextUrl()
052         */
053        public static final String WEB_APP_CONTEXT_URL_PLACEHOLDER = "jnlp.webAppContextUrl";
054    
055        private boolean contextOverride = false;
056        private String fallBackWebAppContextUrl = "http://localhost:8080/";
057        private String jnlpRelativeDirectoryPathFromWebAppContext = "/";
058    
059        /**
060         * Set whether the JNLP properties should override local properties within the application.
061         * Default is "false": JNLP properties settings serve as fallback.
062         * <p>Note that system properties will still override JNLP properties,
063         * if the system properties mode is set to "SYSTEM_PROPERTIES_MODE_OVERRIDE".
064         *
065         * @see #setSystemPropertiesModeName
066         * @see #SYSTEM_PROPERTIES_MODE_OVERRIDE
067         */
068        public void setContextOverride(boolean contextOverride) {
069            this.contextOverride = contextOverride;
070        }
071    
072        /**
073         * Set the webAppContextUrl to use when no javax.jnlp.BasicService is available.
074         * This is usefull for testing outside JNLP (Java webstart).
075         * This defaults to <code>http://localhost:8080/</code>.
076         * Ussually you 'll want to set this to <code>http://localhost:8080/mywebapp/</code>.
077         *
078         * @param fallBackWebAppContextUrl the webAppContextUrl to fall back on ending with a slash
079         */
080        public void setFallBackWebAppContextUrl(String fallBackWebAppContextUrl) {
081            this.fallBackWebAppContextUrl = fallBackWebAppContextUrl;
082        }
083    
084        /**
085         * Sets the relative directory path of the JNLP file relative to the WebAppContext.
086         * Default this is <code>/</code>, which means that the JNLP file is in the root of your webapp.
087         * If your JNLP file isn't in the root of you webapp, change it.
088         * For example for <code>http://localhost:8080/mywebapp/dist/jnlp/myswingapp.jnlp</code>
089         * you would set this to <code>/dist/jnlp/</code>.
090         *
091         * @param jnlpRelativeDirectoryPathFromWebAppContext
092         *         the relative directory path starting and ending with a slash
093         */
094        public void setJnlpRelativeDirectoryPathFromWebAppContext(String jnlpRelativeDirectoryPathFromWebAppContext) {
095            this.jnlpRelativeDirectoryPathFromWebAppContext = jnlpRelativeDirectoryPathFromWebAppContext;
096        }
097    
098        protected String resolvePlaceholder(String placeholder, Properties props) {
099            String value = null;
100            if (this.contextOverride) {
101                value = resolvePlaceholder(placeholder);
102            }
103            if (value == null) {
104                value = super.resolvePlaceholder(placeholder, props);
105            }
106            if (value == null) {
107                value = resolvePlaceholder(placeholder);
108            }
109            return value;
110        }
111    
112        /**
113         * Resolves the given placeholder using the jnlp properties.
114         *
115         * @param placeholder the placeholder to resolve
116         * @return the resolved value, of null if none
117         */
118        protected String resolvePlaceholder(String placeholder) {
119            String value = null;
120            if (placeholder.equals(WEB_APP_CONTEXT_URL_PLACEHOLDER)) {
121                value = getWebAppContextUrl();
122            }
123            return value;
124        }
125    
126        /**
127         * Uses the jnlp API to determine the webapp context.
128         * If used outside of webstart, <code>fallBackWebAppContextUrl</code> is returned.
129         * For example this could return <code>http://localhost:8080/mywebapp/</code>.
130         *
131         * @return the url to the webapp ending with a slash
132         */
133        public String getWebAppContextUrl() {
134            String webAppContextUrl;
135            try {
136                BasicService basicService = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService");
137                String codeBase = basicService.getCodeBase().toExternalForm();
138                if (!codeBase.endsWith("/")) {
139                    codeBase += "/";
140                }
141                int webAppContextUrlLength = codeBase.lastIndexOf(jnlpRelativeDirectoryPathFromWebAppContext);
142                webAppContextUrl = codeBase.substring(0, webAppContextUrlLength + 1);
143            } catch (UnavailableServiceException e) {
144                // TODO logging
145                webAppContextUrl = fallBackWebAppContextUrl;
146            }
147            return webAppContextUrl;
148        }
149    
150    }