001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.image;
017    
018    import java.io.IOException;
019    import java.net.MalformedURLException;
020    import java.net.URL;
021    import java.net.URLConnection;
022    import java.net.URLStreamHandler;
023    import java.net.URLStreamHandlerFactory;
024    
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    import org.springframework.core.io.Resource;
028    import org.springframework.util.Assert;
029    import org.springframework.util.StringUtils;
030    
031    /**
032     * <p>
033     * A URL protocol handler that resolves images from an ImageSource.
034     * </p>
035     * <p>
036     * The syntax of an "image:" URL is: <code>image:{imageKey}</code>
037     * </p>
038     *
039     * There are three methods to register/use this custom protocol:
040     * <ol>
041     * <li>Supply the URLStreamHandler when constructing your URL object.</li>
042     * <li>Create an URLStreamHandlerFactory and register it on the URL class by
043     * using the setURLStreamFactory method.</li>
044     * <li>Create the URLStreamHandler by naming it Handler and placing it in a
045     * package which ends in the name if the protocol. Then register the package
046     * prefix before the protocol name by supplying it to the vm with the property
047     * -Djava.protocol.handler.pkgs. (thus setting it to eg 'my.company.protocols',
048     * your protocol is named image and your class name must be Handler which lives
049     * in package 'my.company.protocols.image') Multiple packages can be supplied by
050     * separating them with a '|'.</li>
051     * </ol>
052     *
053     * Now all of these have drawbacks:
054     *
055     * <ol>
056     * <li>obviously you don't want to construct each URL object with its specific
057     * handler.</li>
058     * <li>the factory can be set only once, if set twice an exception will be
059     * thrown. This was the initial error of this issue.</li>
060     * <li>you need to supply this system parameter at startup. The static method
061     * in the image Handler can only be used if no URL was created before and the
062     * system parameter wasn't read yet.</li>
063     * </ol>
064     *
065     * <p>
066     * We recommend that you use the system parameter at startup to ensure that the
067     * handler is registered:
068     * </p>
069     *
070     * <pre>
071     * -Djava.protocol.handler.pkgs=org.springframework.richclient
072     * </pre>
073     *
074     * <p>
075     * A static method {@link #installImageUrlHandler} is provided that extends the
076     * system property and includes the 'org.springframework.richclient'. This
077     * method can also be triggered by creating an imageSource using
078     * {@link DefaultImageSource#DefaultImageSource(boolean, java.util.Map)}. Note
079     * that this will only work if the system property isn't already read. If an URL
080     * was created and an {@link URLStreamHandlerFactory} is available, extending
081     * the system property won't have any effect.
082     * </p>
083     *
084     * @author Oliver Hutchison
085     * @author Jan Hoskens
086     *
087     */
088    public class Handler extends URLStreamHandler {
089    
090            private static final Log logger = LogFactory.getLog(Handler.class);
091    
092            private static ImageSource urlHandlerImageSource;
093    
094            /**
095             * Installs this class as a handler for the "image:" protocol. Images will
096             * be resolved from the provided image source.
097             */
098            public static void installImageUrlHandler(ImageSource urlHandlerImageSource) {
099                    Assert.notNull(urlHandlerImageSource);
100    
101                    Handler.urlHandlerImageSource = urlHandlerImageSource;
102    
103                    try {
104                            // System properties should be set at JVM startup
105                            // Testcases in IDEA/Eclipse are at JVM startup, but not in Maven's
106                            // surefire...
107                            // TODO this entire implementation should be changed with a
108                            // java.net.URLStreamHandlerFactory instead.
109                            String packagePrefixList = System.getProperty("java.protocol.handler.pkgs");
110                            String newPackagePrefixList = null;
111                            String orgSpringFrameworkRichclientString = "org.springframework.richclient";
112                            if (packagePrefixList == null || packagePrefixList.equals("")) {
113                                    newPackagePrefixList = orgSpringFrameworkRichclientString;
114                            }
115                            else if (("|" + packagePrefixList + "|").indexOf("|" + orgSpringFrameworkRichclientString + "|") < 0) {
116                                    newPackagePrefixList = packagePrefixList + "|" + orgSpringFrameworkRichclientString;
117                            }
118                            if (newPackagePrefixList != null) {
119                                    System.setProperty("java.protocol.handler.pkgs", newPackagePrefixList);
120                            }
121                    }
122                    catch (SecurityException e) {
123                            logger.warn("Unable to install image URL handler", e);
124                            Handler.urlHandlerImageSource = null;
125                    }
126            }
127    
128            /**
129             * Creates an instance of <code>Handler</code>.
130             */
131            public Handler() {
132            }
133    
134            protected URLConnection openConnection(URL url) throws IOException {
135                    if (!StringUtils.hasText(url.getPath())) {
136                            throw new MalformedURLException("must provide an image key.");
137                    }
138                    else if (StringUtils.hasText(url.getHost())) {
139                            throw new MalformedURLException("host part should be empty.");
140                    }
141                    else if (url.getPort() != -1) {
142                            throw new MalformedURLException("port part should be empty.");
143                    }
144                    else if (StringUtils.hasText(url.getQuery())) {
145                            throw new MalformedURLException("query part should be empty.");
146                    }
147                    else if (StringUtils.hasText(url.getRef())) {
148                            throw new MalformedURLException("ref part should be empty.");
149                    }
150                    else if (StringUtils.hasText(url.getUserInfo())) {
151                            throw new MalformedURLException("user info part should be empty.");
152                    }
153                    urlHandlerImageSource.getImage(url.getPath());
154                    Resource image = urlHandlerImageSource.getImageResource(url.getPath());
155                    if (image != null)
156                            return image.getURL().openConnection();
157    
158                    throw new IOException("null image returned for key [" + url.getFile() + "].");
159            }
160    }