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 }