View Javadoc

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 }