Authorized album is now working fully.
[photos] / src / main / java / org / wamblee / photos / model / authorization / AuthorizedAlbum.java
1 /*
2  * Copyright 2005 the original author or authors.
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 org.wamblee.photos.model.authorization;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.logging.Logger;
23
24 import javax.enterprise.context.SessionScoped;
25 import javax.inject.Inject;
26 import javax.servlet.http.HttpSession;
27
28 import org.wamblee.cache.Cache;
29 import org.wamblee.cache.CachedObject;
30 import org.wamblee.photos.model.Album;
31 import org.wamblee.photos.model.Path;
32 import org.wamblee.photos.model.Photo;
33 import org.wamblee.photos.model.PhotoEntry;
34 import org.wamblee.photos.model.plumbing.AllPhotos;
35 import org.wamblee.photos.model.plumbing.AuthorizedPhotos;
36 import org.wamblee.photos.model.plumbing.PhotoCache;
37 import org.wamblee.security.authorization.AllOperation;
38 import org.wamblee.security.authorization.AuthorizationService;
39 import org.wamblee.security.authorization.DeleteOperation;
40 import org.wamblee.security.authorization.ReadOperation;
41 import org.wamblee.security.authorization.WriteOperation;
42
43 /**
44  * Decorator for an album providing defined behavior when used in a concurrent
45  * setting.
46  */
47 @SessionScoped
48 @AuthorizedPhotos
49 public class AuthorizedAlbum extends AuthorizedPhotoEntry implements Album {
50
51     private static final Logger LOGGER = Logger.getLogger(AuthorizedAlbum.class
52         .getName());
53
54     private AuthorizationService _authorizer;
55
56     private CachedObject<String, ArrayList<PhotoEntry>> _authorizedEntries;
57
58     private HttpSession _session;
59
60     protected AuthorizedAlbum() {
61         super(null);
62         // for CDI
63     }
64
65     /**
66      * Constructs concurrent album as a decorator for an album implementation.
67      * 
68      * @param aAlbum
69      *            Album to decorate.
70      */
71     @Inject
72     public AuthorizedAlbum(@AllPhotos Album aAlbum,
73         AuthorizationService aService,
74         @PhotoCache Cache<String, ArrayList<PhotoEntry>> aCache,
75         HttpSession aSession) {
76         super(aAlbum);
77         _authorizer = aService;
78         _authorizedEntries = new CachedObject<String, ArrayList<PhotoEntry>>(
79             aCache, aSession.getId() + "/" + aAlbum.getPath(),
80             new CachedObject.Computation<String, ArrayList<PhotoEntry>>() {
81                 public ArrayList<PhotoEntry> getObject(String aObjectKey) {
82                     return AuthorizedAlbum.this.compute();
83                 }
84             });
85         _session = aSession;
86     }
87
88     /**
89      * Computes the cache of photo entries to which read access is allowed.
90      * 
91      * @return Photo entries to which read access is allowed.
92      */
93     private synchronized ArrayList<PhotoEntry> compute() {
94         LOGGER.info("Refreshing cache " + getPath());
95         ArrayList<PhotoEntry> result = new ArrayList<PhotoEntry>();
96         for (int i = 0; i < decorated().size(); i++) {
97             PhotoEntry entry = decorated().getEntry(i);
98             if (_authorizer.isAllowed(entry, new ReadOperation())) {
99                 result.add(decorate(entry)); // subscription will take place
100                 // automatically.
101             }
102         }
103         return result;
104     }
105
106     private Album decorated() {
107         return (Album) getEntry();
108     }
109
110     /**
111      * Creates a decorate for the photo entry to make it safe for concurrent
112      * access.
113      * 
114      * @param aEntry
115      *            Entry to decorate
116      * @return Decorated photo.
117      */
118     private <T extends PhotoEntry> T decorate(T aEntry) {
119         if (aEntry == null) {
120             return null;
121         } else if (aEntry instanceof Photo) {
122             return (T) new AuthorizedPhoto((Photo) aEntry);
123         } else if (aEntry instanceof Album) {
124             return (T) new AuthorizedAlbum((Album) aEntry, _authorizer,
125                 _authorizedEntries.getCache(), _session);
126         } else {
127             throw new IllegalArgumentException(
128                 "Entry is neither a photo nor an album: " + aEntry);
129         }
130     }
131
132     /*
133      * (non-Javadoc)
134      * 
135      * @see org.wamblee.photos.model.Album#getEntry(java.lang.String)
136      */
137     public PhotoEntry getEntry(String aPath) {
138         return getEntry(new Path(aPath));
139     }
140
141     /*
142      * (non-Javadoc)
143      * 
144      * @see org.wamblee.photos.model.Album#getEntry(org.wamblee.photos.model.Path)
145      */
146     public PhotoEntry getEntry(Path aPath) {
147         if (aPath.isRoot()) {
148             return this;
149         }
150         List<PhotoEntry> cache = _authorizedEntries.get();
151         String id = aPath.getPart(0);
152         Path remainder = aPath.remainder();
153         for (PhotoEntry entry : cache) {
154             if (entry.getId().equals(id)) {
155                 if (remainder.isRoot()) {
156                     return entry;
157                 } else {
158                     if (!(entry instanceof Album)) {
159                         throw new IllegalArgumentException(getPath() + " " +
160                             aPath);
161                     }
162                     return ((Album) entry).getEntry(remainder);
163                 }
164             }
165         }
166         return null;
167     }
168
169     /*
170      * (non-Javadoc)
171      * 
172      * @see org.wamblee.photos.model.Album#getEntry(int)
173      */
174     public PhotoEntry getEntry(int aIndex) {
175         return _authorizedEntries.get().get(aIndex);
176     }
177
178     /*
179      * (non-Javadoc)
180      * 
181      * @see org.wamblee.photos.model.Album#size()
182      */
183     public int size() {
184         return _authorizedEntries.get().size();
185     }
186
187     /*
188      * (non-Javadoc)
189      * 
190      * @see org.wamblee.photos.model.Album#addImage(java.lang.String,
191      *      java.io.InputStream)
192      */
193     public void addImage(String aId, InputStream aImage) throws IOException {
194         _authorizer.check(this, new WriteOperation());
195         _authorizedEntries.invalidate();
196         decorated().addImage(aId, aImage);
197     }
198
199     /*
200      * (non-Javadoc)
201      * 
202      * @see org.wamblee.photos.model.Album#addAlbum(java.lang.String)
203      */
204     public void addAlbum(String aId) throws IOException {
205         _authorizer.check(this, new WriteOperation());
206         _authorizedEntries.invalidate();
207         decorated().addAlbum(aId);
208     }
209
210     /*
211      * (non-Javadoc)
212      * 
213      * @see org.wamblee.photos.model.Album#removeEntry(java.lang.String)
214      */
215     public void removeEntry(String aId) throws IOException {
216         // Check whether deletion is allowed.
217         PhotoEntry entry = _authorizer.check(decorated().getEntry("/" + aId),
218             new DeleteOperation());
219         _authorizedEntries.invalidate();
220         decorated().removeEntry(aId);
221     }
222
223     public Photo findPhotoBefore(String aId) {
224         Photo entry = decorated().findPhotoBefore(aId);
225         while (entry != null &&
226             !_authorizer.isAllowed(entry, new AllOperation())) {
227             entry = decorated().findPhotoBefore(entry.getId());
228         }
229         return decorate(entry);
230     }
231
232     public Photo findPhotoAfter(String aId) {
233         Photo entry = decorated().findPhotoAfter(aId);
234         while (entry != null &&
235             !_authorizer.isAllowed(entry, new AllOperation())) {
236             entry = decorated().findPhotoAfter(entry.getId());
237         }
238         return decorate(entry);
239     }
240
241     /*
242      * (non-Javadoc)
243      * 
244      * @see java.lang.Object#toString()
245      */
246     @Override
247     public String toString() {
248         return "AuthorizedAlbum(path = '" + decorated().getPath() + "')";
249     }
250 }