001/*
002 * Copyright 2002-2012 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 *      https://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
017package org.springframework.web.portlet.context;
018
019import java.io.File;
020import java.io.FileNotFoundException;
021import java.io.IOException;
022import java.io.InputStream;
023import java.net.MalformedURLException;
024import java.net.URL;
025import javax.portlet.PortletContext;
026
027import org.springframework.core.io.AbstractFileResolvingResource;
028import org.springframework.core.io.ContextResource;
029import org.springframework.core.io.Resource;
030import org.springframework.util.Assert;
031import org.springframework.util.ResourceUtils;
032import org.springframework.util.StringUtils;
033import org.springframework.web.portlet.util.PortletUtils;
034
035/**
036 * {@link org.springframework.core.io.Resource} implementation for
037 * {@link javax.portlet.PortletContext} resources, interpreting
038 * relative paths within the portlet application root directory.
039 *
040 * <p>Always supports stream access and URL access, but only allows
041 * {@code java.io.File} access when the portlet application archive
042 * is expanded.
043 *
044 * @author Juergen Hoeller
045 * @author John A. Lewis
046 * @since 2.0
047 * @see javax.portlet.PortletContext#getResourceAsStream
048 * @see javax.portlet.PortletContext#getRealPath
049 */
050public class PortletContextResource extends AbstractFileResolvingResource implements ContextResource {
051
052        private final PortletContext portletContext;
053
054        private final String path;
055
056
057        /**
058         * Create a new PortletContextResource.
059         * <p>The Portlet spec requires that resource paths start with a slash,
060         * even if many containers accept paths without leading slash too.
061         * Consequently, the given path will be prepended with a slash if it
062         * doesn't already start with one.
063         * @param portletContext the PortletContext to load from
064         * @param path the path of the resource
065         */
066        public PortletContextResource(PortletContext portletContext, String path) {
067                // check PortletContext
068                Assert.notNull(portletContext, "Cannot resolve PortletContextResource without PortletContext");
069                this.portletContext = portletContext;
070
071                // check path
072                Assert.notNull(path, "Path is required");
073                String pathToUse = StringUtils.cleanPath(path);
074                if (!pathToUse.startsWith("/")) {
075                        pathToUse = "/" + pathToUse;
076                }
077                this.path = pathToUse;
078        }
079
080
081        /**
082         * Return the PortletContext for this resource.
083         */
084        public final PortletContext getPortletContext() {
085                return this.portletContext;
086        }
087
088        /**
089         * Return the path for this resource.
090         */
091        public final String getPath() {
092                return this.path;
093        }
094
095        /**
096         * This implementation checks {@code PortletContext.getResource}.
097         * @see javax.portlet.PortletContext#getResource(String)
098         */
099        @Override
100        public boolean exists() {
101                try {
102                        URL url = this.portletContext.getResource(this.path);
103                        return (url != null);
104                }
105                catch (MalformedURLException ex) {
106                        return false;
107                }
108        }
109
110        /**
111         * This implementation delegates to {@code PortletContext.getResourceAsStream},
112         * which returns {@code null} in case of a non-readable resource (e.g. a directory).
113         * @see javax.portlet.PortletContext#getResourceAsStream(String)
114         */
115        @Override
116        public boolean isReadable() {
117                InputStream is = this.portletContext.getResourceAsStream(this.path);
118                if (is != null) {
119                        try {
120                                is.close();
121                        }
122                        catch (IOException ex) {
123                                // ignore
124                        }
125                        return true;
126                }
127                else {
128                        return false;
129                }
130        }
131
132        /**
133         * This implementation delegates to {@code PortletContext.getResourceAsStream},
134         * but throws a FileNotFoundException if not found.
135         * @see javax.portlet.PortletContext#getResourceAsStream(String)
136         */
137        @Override
138        public InputStream getInputStream() throws IOException {
139                InputStream is = this.portletContext.getResourceAsStream(this.path);
140                if (is == null) {
141                        throw new FileNotFoundException("Could not open " + getDescription());
142                }
143                return is;
144        }
145
146        /**
147         * This implementation delegates to {@code PortletContext.getResource},
148         * but throws a FileNotFoundException if no resource found.
149         * @see javax.portlet.PortletContext#getResource(String)
150         */
151        @Override
152        public URL getURL() throws IOException {
153                URL url = this.portletContext.getResource(this.path);
154                if (url == null) {
155                        throw new FileNotFoundException(
156                                        getDescription() + " cannot be resolved to URL because it does not exist");
157                }
158                return url;
159        }
160
161        /**
162         * This implementation resolves "file:" URLs or alternatively delegates to
163         * {@code PortletContext.getRealPath}, throwing a FileNotFoundException
164         * if not found or not resolvable.
165         * @see javax.portlet.PortletContext#getResource(String)
166         * @see javax.portlet.PortletContext#getRealPath(String)
167         */
168        @Override
169        public File getFile() throws IOException {
170                URL url = getURL();
171                if (ResourceUtils.isFileURL(url)) {
172                        // Proceed with file system resolution...
173                        return super.getFile();
174                }
175                else {
176                        String realPath = PortletUtils.getRealPath(this.portletContext, this.path);
177                        return new File(realPath);
178                }
179        }
180
181        @Override
182        public Resource createRelative(String relativePath) {
183                String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
184                return new PortletContextResource(this.portletContext, pathToUse);
185        }
186
187        @Override
188        public String getFilename() {
189                return StringUtils.getFilename(this.path);
190        }
191
192        @Override
193        public String getDescription() {
194                return "PortletContext resource [" + this.path + "]";
195        }
196
197        @Override
198        public String getPathWithinContext() {
199                return this.path;
200        }
201
202
203        @Override
204        public boolean equals(Object obj) {
205                if (obj == this) {
206                        return true;
207                }
208                if (obj instanceof PortletContextResource) {
209                        PortletContextResource otherRes = (PortletContextResource) obj;
210                        return (this.portletContext.equals(otherRes.portletContext) && this.path.equals(otherRes.path));
211                }
212                return false;
213        }
214
215        @Override
216        public int hashCode() {
217                return this.path.hashCode();
218        }
219
220}