1 package org.alfresco.maven.plugin.amp.util;
2
3 import java.io.IOException;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.Map;
7 import java.util.Set;
8
9 /***
10 * Represents the structure of a web application composed of multiple
11 * overlays. Each overlay is registered within this structure with the
12 * set of files it holds.
13 * <p/>
14 * Note that this structure is persisted to disk at each invocation to
15 * store wich owner holds which path (file).
16 *
17 * @author Stephane Nicoll
18 */
19 public class AmpStructure
20 {
21
22 private Map registeredFiles;
23
24 private transient PathSet allFiles = new PathSet();
25
26 private transient AmpStructure cache;
27
28 /***
29 * Creates a new empty instance.
30 */
31 public AmpStructure()
32 {
33 this.registeredFiles = new HashMap();
34 this.cache = null;
35 }
36
37 /***
38 * Creates a new instance with the specified cache.
39 *
40 * @param cache the cache
41 */
42 public AmpStructure( AmpStructure cache )
43 {
44 this.registeredFiles = new HashMap();
45 if ( cache == null )
46 {
47 this.cache = new AmpStructure();
48 }
49 else
50 {
51 this.cache = cache;
52 }
53 }
54
55
56 /***
57 * Specify if the specified <tt>path</tt> is registered or not.
58 *
59 * @param path the relative path from the webapp root directory
60 * @return true if the path is registered, false otherwise
61 */
62 public boolean isRegistered( String path )
63 {
64 return getFullStructure().contains( path );
65
66 }
67
68 /***
69 * Registers the specified path for the specified owner. Returns <tt>true</tt>
70 * if the path is not already registered, <tt>false</tt> otherwise.
71 *
72 * @param id the owner of the path
73 * @param path the relative path from the webapp root directory
74 * @return true if the file was registered successfully
75 */
76 public boolean registerFile( String id, String path )
77 {
78 if ( !isRegistered( path ) )
79 {
80 doRegister( id, path );
81 return true;
82 }
83 else
84 {
85 return false;
86 }
87 }
88
89 /***
90 * Registers the specified path for the specified owner. Invokes
91 * the <tt>callback</tt> with the result of the registration.
92 *
93 * @param id the owner of the path
94 * @param path the relative path from the webapp root directory
95 * @param callback the callback to invoke with the result of the registration
96 * @throws IOException if the callback invocation throws an IOException
97 */
98 public void registerFile( String id, String path, RegistrationCallback callback )
99 throws IOException
100 {
101
102
103 if ( isRegistered( path ) )
104 {
105 callback.refused( id, path, getOwner( path ) );
106 }
107 else
108 {
109 doRegister( id, path );
110
111 if ( cache.getOwner( path ) == null )
112 {
113 callback.registered( id, path );
114
115 }
116 else if ( cache.getOwner( path ).equals( id ) )
117 {
118 callback.alreadyRegistered( id, path );
119 }
120 else if ( getOwners().contains( cache.getOwner( path ) ) )
121 {
122 callback.superseded( id, path, cache.getOwner( path ) );
123 }
124 else
125 {
126 callback.supersededUnknownOwner( id, path, cache.getOwner( path ) );
127 }
128 }
129 }
130
131 /***
132 * Returns the owner of the specified <tt>path</tt>. If the file is not
133 * registered, returns <tt>null</tt>
134 *
135 * @param path the relative path from the webapp root directory
136 * @return the owner or <tt>null</tt>.
137 */
138 public String getOwner( String path )
139 {
140 if ( !isRegistered( path ) )
141 {
142 return null;
143 }
144 else
145 {
146 final Iterator it = registeredFiles.keySet().iterator();
147 while ( it.hasNext() )
148 {
149 final String owner = (String) it.next();
150 final PathSet structure = getStructure( owner );
151 if ( structure.contains( path ) )
152 {
153 return owner;
154 }
155
156 }
157 throw new IllegalStateException(
158 "Should not happen, path[" + path + "] is flagged as being registered but was not found." );
159 }
160
161 }
162
163 /***
164 * Returns the owners. Note that this the returned {@link Set} may be
165 * inconsistent since it represents a persistent cache accross multiple
166 * invocations.
167 * <p/>
168 * For instance, if an overlay was removed in this execution, it will be
169 * still be there till the cache is cleaned. This happens when the clean
170 * mojo is invoked.
171 *
172 * @return the list of owners
173 */
174 public Set getOwners()
175 {
176 return registeredFiles.keySet();
177 }
178
179 /***
180 * Returns all paths that have been registered so far.
181 *
182 * @return all registered path
183 */
184 public PathSet getFullStructure()
185 {
186 return allFiles;
187 }
188
189 /***
190 * Returns the list of registered files for the specified owner.
191 *
192 * @param id the owner
193 * @return the list of files registered for that owner
194 */
195 public PathSet getStructure( String id )
196 {
197 PathSet pathSet = (PathSet) registeredFiles.get( id );
198 if ( pathSet == null )
199 {
200 pathSet = new PathSet();
201 registeredFiles.put( id, pathSet );
202 }
203 return pathSet;
204 }
205
206 private void doRegister( String id, String path )
207 {
208 getFullStructure().add( path );
209 getStructure( id ).add( path );
210 }
211
212 private Object readResolve()
213 {
214
215 this.allFiles = new PathSet();
216 final Iterator it = registeredFiles.values().iterator();
217 while ( it.hasNext() )
218 {
219 PathSet pathSet = (PathSet) it.next();
220 this.allFiles.addAll( pathSet );
221 }
222 return this;
223 }
224
225 /***
226 * Callback interfce to handle events related to filepath registration in
227 * the webapp.
228 */
229 public interface RegistrationCallback
230 {
231
232
233 /***
234 * Called if the <tt>targetFilename</tt> for the specified <tt>ownerId</tt>
235 * has been registered successfully.
236 * <p/>
237 * This means that the <tt>targetFilename</tt> was unknown and has been
238 * registered successfully.
239 *
240 * @param ownerId the ownerId
241 * @param targetFilename the relative path according to the root of the webapp
242 * @throws IOException if an error occured while handling this event
243 */
244 void registered( String ownerId, String targetFilename )
245 throws IOException;
246
247 /***
248 * Called if the <tt>targetFilename</tt> for the specified <tt>ownerId</tt>
249 * has already been registered.
250 * <p/>
251 * This means that the <tt>targetFilename</tt> was known and belongs to the
252 * specified owner.
253 *
254 * @param ownerId the ownerId
255 * @param targetFilename the relative path according to the root of the webapp
256 * @throws IOException if an error occured while handling this event
257 */
258 void alreadyRegistered( String ownerId, String targetFilename )
259 throws IOException;
260
261 /***
262 * Called if the registration of the <tt>targetFilename</tt> for the
263 * specified <tt>ownerId</tt> has been refused since the path already
264 * belongs to the <tt>actualOwnerId</tt>.
265 * <p/>
266 * This means that the <tt>targetFilename</tt> was known and does not
267 * belong to the specified owner.
268 *
269 * @param ownerId the ownerId
270 * @param targetFilename the relative path according to the root of the webapp
271 * @param actualOwnerId the actual owner
272 * @throws IOException if an error occured while handling this event
273 */
274 void refused( String ownerId, String targetFilename, String actualOwnerId )
275 throws IOException;
276
277 /***
278 * Called if the <tt>targetFilename</tt> for the specified <tt>ownerId</tt>
279 * has been registered successfully by superseding a <tt>deprecatedOwnerId</tt>,
280 * that is the previous owner of the file.
281 * <p/>
282 * This means that the <tt>targetFilename</tt> was known but for another
283 * owner. This usually happens after a project's configuration change. As a
284 * result, the file has been registered successfully to the new owner.
285 *
286 * @param ownerId the ownerId
287 * @param targetFilename the relative path according to the root of the webapp
288 * @param deprecatedOwnerId the previous owner that does not exist anymore
289 * @throws IOException if an error occured while handling this event
290 */
291 void superseded( String ownerId, String targetFilename, String deprecatedOwnerId )
292 throws IOException;
293
294 /***
295 * Called if the <tt>targetFilename</tt> for the specified <tt>ownerId</tt>
296 * has been registered successfully by superseding a <tt>unknownOwnerId</tt>,
297 * that is an owner that does not exist anymore in the current project.
298 * <p/>
299 * This means that the <tt>targetFilename</tt> was known but for an owner that
300 * does not exist anymore. Hence the file has been registered successfully to
301 * the new owner.
302 *
303 * @param ownerId the ownerId
304 * @param targetFilename the relative path according to the root of the webapp
305 * @param unknownOwnerId the previous owner that does not exist anymore
306 * @throws IOException if an error occured while handling this event
307 */
308 void supersededUnknownOwner( String ownerId, String targetFilename, String unknownOwnerId )
309 throws IOException;
310
311 }
312 }