1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.jguiraffe.locators;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.net.URLConnection;
23 import java.net.URLStreamHandler;
24 import java.util.concurrent.atomic.AtomicReference;
25
26 /**
27 * <p>
28 * An abstract base class for <code>Locator</code> implementations that mainly
29 * operate on streams.
30 * </p>
31 * <p>
32 * The <code>Locator</code> interface requires that the <code>getURL()</code>
33 * method has to be implemented in a meaningful way. However, there are some use
34 * cases where data is available only as a stream (e.g. in some binary form),
35 * and it is not obvious how a URL can be constructed to point to this data.
36 * This class is designed to support such use cases.
37 * </p>
38 * <p>
39 * The basic idea is that this class provides a specialized implementation of
40 * the abstract <code>java.net.URLStreamHandler</code> class. This
41 * implementation can create <code>java.net.URLConnection</code> objects whose
42 * <code>getInputStream()</code> method returns the input stream provided by the
43 * concrete <code>Locator</code> implementation.
44 * </p>
45 * <p>
46 * Concrete subclasses have to implement the <code>getInputStream()</code>
47 * method to return the stream they point to. They also have to provide an
48 * implementation of the <code>createURL()</code> method. This method is passed
49 * a <code>URLStreamHandler</code> object as described above. An implementation
50 * of <code>createURL()</code> can create a URL with an arbitrary protocol and
51 * other components as it sees fit. As long as the <code>URLStreamHandler</code>
52 * is used when creating the URL it is guaranteed that calls to
53 * <code>openConnection()</code> or <code>openStream()</code> on the URL object
54 * return the stream of the <code>Locator</code>.
55 * </p>
56 *
57 * @author Oliver Heger
58 * @version $Id: AbstractStreamLocator.java 205 2012-01-29 18:29:57Z oheger $
59 */
60 public abstract class AbstractStreamLocator extends AbstractLocator
61 {
62 /** Stores the URL managed by this locator. */
63 private final AtomicReference<URL> urlReference;
64
65 /**
66 * Creates a new instance of <code>AbstractStreamLocator</code>.
67 */
68 protected AbstractStreamLocator()
69 {
70 urlReference = new AtomicReference<URL>();
71 }
72
73 /**
74 * Returns the <code>URL</code> managed by this locator. On first access
75 * this implementation delegates to <code>createURL()</code> for creating
76 * the URL. Then the URL is cached and directly returned on subsequent
77 * requests. Note that a non-blocking algorithm is used for lazily creating
78 * the URL. So on concurrent access it may happen that
79 * <code>createURL()</code> is invoked multiple times. However, it is
80 * guaranteed that this method always returns the same <code>URL</code>.
81 *
82 * @return the <code>URL</code> managed by this locator
83 */
84 public URL getURL()
85 {
86 URL url = urlReference.get();
87
88 if (url == null)
89 {
90 // The URL has not been created yet, so do it now.
91 try
92 {
93 url = createURL(new StreamLocatorURLStreamHandler());
94 }
95 catch (MalformedURLException mex)
96 {
97 throw new LocatorException(mex);
98 }
99 if (!urlReference.compareAndSet(null, url))
100 {
101 // In the meantime the URL was created by another thread.
102 url = urlReference.get();
103 }
104 }
105
106 return url;
107 }
108
109 /**
110 * Creates the URL managed by this locator. This method is called by
111 * <code>getURL()</code> to initialize the URL. An implementation is free to
112 * create whatever URL it likes, but it should use the specified
113 * <code>URLStreamHandler</code>. This handler ensures that reading from the
114 * URL is delegated to the stream managed by this locator.
115 *
116 * @param streamHandler the stream handler to use when creating the URL
117 * @return the URL to be returned by <code>getURL()</code>
118 * @throws MalformedURLException if the URL cannot be created
119 */
120 protected abstract URL createURL(URLStreamHandler streamHandler)
121 throws MalformedURLException;
122
123 /**
124 * A specialized URLConnection implementation that always returns the stream
125 * managed by this locator as input stream. This allows this locator class
126 * to return a URL that can be used in the usual way, but actually points to
127 * the data managed by this locator.
128 */
129 private class StreamURLConnection extends URLConnection
130 {
131 /**
132 * Creates a new instance of <code>StreamURLConnection</code>.
133 *
134 * @param url the URL
135 */
136 public StreamURLConnection(URL url)
137 {
138 super(url);
139 }
140
141 /**
142 * Dummy implementation of this method. We do not make any connection,
143 * but always return the stream managed by this locator.
144 *
145 * @throws IOException if an error occurs
146 */
147 @Override
148 public void connect() throws IOException
149 {
150 }
151
152 /**
153 * Returns the input stream for this URL. This is actually the stream of
154 * the locator.
155 *
156 * @return an input stream for reading from this URL
157 * @throws IOException if an error occurs
158 */
159 @Override
160 public InputStream getInputStream() throws IOException
161 {
162 return AbstractStreamLocator.this.getInputStream();
163 }
164 }
165
166 /**
167 * A specialized URLStreamHandler implementation. This implementation
168 * creates URLConnection objects for URLs that access the input stream
169 * managed by this locator.
170 */
171 private class StreamLocatorURLStreamHandler extends URLStreamHandler
172 {
173 /**
174 * Returns a connection object for the specified URL. This
175 * implementation returns the fake connection class that always accesses
176 * this locator's input stream.
177 *
178 * @param u the URL
179 * @return a connection for this URL
180 * @throws IOException if an error occurs
181 */
182 @Override
183 protected URLConnection openConnection(URL u) throws IOException
184 {
185 return new StreamURLConnection(u);
186 }
187 }
188 }