001    /*
002     * Copyright 2005 Niclas Hedhman
003     * Copyright 2005 Stephen J. McConnell.
004     *
005     * Licensed  under the  Apache License,  Version 2.0  (the "License");
006     * you may not use  this file  except in  compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *   http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed  under the  License is distributed on an "AS IS" BASIS,
013     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
014     * implied.
015     *
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    package net.dpml.transit.link;
021    
022    import net.dpml.util.StreamUtils;
023    
024    import java.io.ByteArrayInputStream;
025    import java.io.ByteArrayOutputStream;
026    import java.io.IOException;
027    import java.io.InputStream;
028    import java.io.OutputStream;
029    import java.net.URI;
030    import java.net.URL;
031    import java.security.AccessController;
032    import java.security.PrivilegedActionException;
033    import java.security.PrivilegedExceptionAction;
034    
035    import net.dpml.transit.Artifact;
036    import net.dpml.transit.NullArgumentException;
037    import net.dpml.transit.artifact.ArtifactNotFoundException;
038    import net.dpml.util.PropertyResolver;
039    
040    /** 
041     * A link manager that maintains persistent link information as a resource.
042     * Link resource located using the [cache]/[group]/[name]/[type]s/[name]-[version].[type].link
043     * resource naming convention.
044     * 
045     * Applications should not call the methods for the LinkManager directly,
046     * and it is likely that the LinkManager remains outside the reachability of
047     * applications.
048     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
049     * @version 1.0.3
050     */
051    public class ArtifactLinkManager
052        implements LinkManager
053    {
054        /** 
055         * Sets the URI for the provided Link.
056         * The LinkManager is required to persist this information between
057         * JVM restarts and should be persisted on a scope larger than a
058         * single JVM, typically a host or a local area network. LinkManagers
059         * are encouraged to establish other virtual scopes independent of
060         * network topologies.
061         *
062         * @param linkUri the uri of the link resource
063         * @param targetUri the uri that the link redirects to
064         * @exception IOException if the mapping could not be updated.
065         */
066        public void setTargetURI( final URI linkUri, final URI targetUri )
067            throws IOException
068        {
069            if( null == linkUri )
070            {
071                throw new NullArgumentException( "linkUri" );
072            }
073    
074            try
075            {
076                AccessController.doPrivileged( 
077                  new PrivilegedExceptionAction()
078                  {
079                    public Object run()
080                        throws IOException
081                    {
082                        String artifact = linkUri.toASCIIString();
083                        URL store = new URL( null, artifact, new net.dpml.transit.artifact.Handler() );
084                        OutputStream out = store.openConnection().getOutputStream();
085                        byte[] array = getByteArray( targetUri );
086                        ByteArrayInputStream in = new ByteArrayInputStream( array );
087                        StreamUtils.copyStream( in, out, true );
088                        return null; // nothing to return
089                    }
090                  }
091                );
092            } 
093            catch( PrivilegedActionException e )
094            {
095                throw (IOException) e.getException();
096            }
097        }
098    
099        /** 
100         * Returns the URI that the provided link URI instance is pointing to.
101         * @param linkUri the link uri from which the target will be resolved
102         * @exception LinkNotFoundException if the supplied link uri could not be located
103         * @exception IOException if the mapping could not be retrieved, due to
104         *    an IOException during link retrival.
105         * @return target URI that the link points to (possibly null if the link does 
106         *    not declare a target)
107         */
108        public URI getTargetURI( final URI linkUri )
109            throws IOException, LinkNotFoundException
110        {
111            try
112            {
113                URI result = (URI) AccessController.doPrivileged( 
114                  new PrivilegedExceptionAction()
115                  {
116                    public Object run()
117                        throws IOException
118                    {
119                        URL store = null;
120                        if( Artifact.isRecognized( linkUri ) )
121                        {
122                            String artifact = linkUri.toASCIIString();
123                            store = new URL( null, artifact, new net.dpml.transit.artifact.Handler() );
124                        }
125                        else
126                        {
127                            store = linkUri.toURL();
128                        }
129                        
130                        ByteArrayOutputStream out = new ByteArrayOutputStream();
131                        InputStream in = store.openConnection().getInputStream();
132                        StreamUtils.copyStream( in, out, true );
133                        String target = out.toString( "ISO8859-1" );
134                        String path = PropertyResolver.resolve( target );
135                        URI value = URI.create( path );
136                        return value;
137                    }
138                  }
139                );
140                return result;
141            }
142            catch( PrivilegedActionException e )
143            {
144                Exception exception = e.getException();
145                if( exception instanceof ArtifactNotFoundException )
146                {
147                     final String error =
148                       "Link not found: "
149                       + linkUri;
150                     throw new LinkNotFoundException( error, linkUri );
151                }
152                else
153                {
154                     throw (IOException) exception;
155                }
156            }
157        }
158    
159        private byte[] getByteArray( URI uri ) throws IOException
160        {
161            if( null != uri )
162            {
163                return uri.toString().getBytes( "ISO8859-1" );
164            }
165            else
166            {
167                return new byte[0];
168            }
169        }
170    }