1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.store.tree;
12
13 import org.astrogrid.community.User;
14 import org.astrogrid.community.common.exception.CommunitySecurityException;
15 import org.astrogrid.community.common.exception.CommunityServiceException;
16 import org.astrogrid.community.common.ivorn.CommunityIvornParser;
17 import org.astrogrid.community.common.security.data.SecurityToken;
18 import org.astrogrid.community.resolver.CommunityAccountSpaceResolver;
19 import org.astrogrid.community.resolver.CommunityPasswordResolver;
20 import org.astrogrid.store.Ivorn;
21 import org.astrogrid.store.VoSpaceClient;
22 import org.astrogrid.store.delegate.StoreClient;
23 import org.astrogrid.store.delegate.StoreFile;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.Map;
35 import java.util.StringTokenizer;
36
37 /*** Implementation of the TreeClient interfaces against the Iteration6 VoSpace client.
38 *
39 * bit fiddly in places - as the underlying storeClient and StoreFile are broken, fragile and inconsistent. Which means that we need to do a lot of fiddling with path expressions
40 * to get it to do what we want.
41 *
42 * The call to {@link #getRoot()} gets the entire file tree for the user's myspace. We then cache this, and don't requery the server again - opoerations to create files, etc, are mirrored in our file tree.
43 * the view can be synchronized from the server by calling getRoot again.
44 *
45 *
46 * NB: noticed that there's no way to delete a file / folder - odd.
47 * @author Noel Winstanley nw@jb.man.ac.uk 05-Nov-2004
48 */
49 public class IterationSixTreeClient implements TreeClient {
50 /***
51 * Commons Logger for this class
52 */
53 private static final Log logger = LogFactory
54 .getLog(IterationSixTreeClient.class);
55
56 /*** Construct a new tree client
57 *
58 */
59 public IterationSixTreeClient() {
60
61 }
62
63 protected SecurityToken token;
64 protected final CommunityPasswordResolver security = new CommunityPasswordResolver();
65 protected final CommunityAccountSpaceResolver spaceResolver = new CommunityAccountSpaceResolver();
66 protected StoreClient client;
67 protected String username;
68
69
70 public void login(Ivorn communityIvorn, String password)
71 throws TreeClientLoginException, TreeClientServiceException {
72 if (communityIvorn == null) {
73 throw new IllegalArgumentException("Null account");
74 }
75 if (password == null) {
76 throw new IllegalArgumentException("Null password");
77 }
78 logger.info("Logging in " + communityIvorn );
79 try {
80 logger.info("Checking password");
81 this.token = security.checkPassword(communityIvorn.toString(),password);
82 logger.debug(token);
83 CommunityIvornParser ivornParser = new CommunityIvornParser(this.token.getAccount());
84
85 logger.info("user object");
86 username = ivornParser.getAccountName() ;
87 User u = new User();
88 u.setAccount(username+ "@" + ivornParser.getCommunityName());
89 u.setToken(this.token.getToken());
90 logger.debug(u);
91
92 logger.info("Creating vospace client");
93 VoSpaceClient voSpace = new VoSpaceClient(u);
94
95 logger.info("Creating Store Client");
96 Ivorn vospaceIvorn = spaceResolver.resolve(communityIvorn);
97 logger.debug(vospaceIvorn);
98
99 if (vospaceIvorn.toString().endsWith("null")) {
100 String s = vospaceIvorn.toString();
101 vospaceIvorn = new Ivorn(s.substring(0,s.lastIndexOf("null")));
102 logger.warn("mangled vospaceIvorn to " + vospaceIvorn);
103 }
104 client = voSpace.getDelegate(vospaceIvorn);
105 logger.debug(client);
106
107 } catch (CommunitySecurityException e) {
108 logger.info("login failed",e);
109 throw new TreeClientLoginException("Login failed",e);
110 } catch (CommunityServiceException e ) {
111 logger.info("login failed",e);
112 throw new TreeClientLoginException("Login failed",e);
113 } catch (Throwable e) {
114 logger.error("Failed to perform request",e);
115 throw new TreeClientServiceException("Failed to perform request",e);
116 }
117 }
118
119 public StoreClient getStoreClient() {
120 return client;
121 }
122
123 /***
124 * @todo need to call any kind of logout method?
125 */
126 public void logout() throws TreeClientServiceException {
127 logger.info("logging out");
128 this.token = null;
129 this.client = null;
130
131 }
132
133
134 public SecurityToken getToken() {
135 return this.token;
136 }
137
138 /*** queries server, rebuilds tree.
139 */
140 public Container getRoot()
141 throws TreeClientSecurityException,
142 TreeClientServiceException {
143 if (this.token == null) {
144 throw new TreeClientSecurityException("Not logged in");
145 }
146 try {
147 String rootPath = "/" + username + "*";
148 logger.info("Getting root via query " + rootPath);
149 StoreFile container = client.getFiles(rootPath);
150 assert container != null: "Null container returned";
151 StoreFile[] children = container.listFiles();
152 assert children.length == 1 : "Found multiple roots";
153 StoreFile root = children[0];
154 assert root != null : "Root is null";
155 return new IterationSixContainer(root);
156 } catch (IOException e) {
157 logger.fatal("could not get root",e);
158 throw new TreeClientServiceException("Could not get root",e);
159 }
160 }
161
162
163 /*** helper function for string mangling */
164 protected String dropTrailingSlash(String s) {
165 if (s.endsWith("/")) {
166 return s.substring(0,s.length()-1);
167 } else {
168 return s;
169 }
170 }
171
172 /***
173 * Iteration-6 version of a node.
174 * @author Noel Winstanley nw@jb.man.ac.uk 05-Nov-2004
175 *
176 *@modified noel #890 made this public, so AladinAdapter interface can cast and get to the path.
177 * once we drop aladinAdapter, can make this protected again.
178
179 */
180 public class IterationSixNode implements Node {
181
182 public IterationSixNode(StoreFile wrapped, String path) {
183 this.wrapped = wrapped;
184 this.path = path;
185 logger.debug("Creating iteration six node for:" + path);
186 }
187
188 /*** compute path from wrapped StoreFile, mangling the result */
189 public IterationSixNode(StoreFile wrapped) {
190 this(wrapped,"/" + dropTrailingSlash(wrapped.getPath()).trim());
191 }
192
193 protected final StoreFile wrapped;
194 protected final String path;
195
196 public String getName() {
197 return wrapped.getName().trim();
198 }
199
200 public boolean isFile() {
201 return wrapped.isFile();
202 }
203
204 public boolean isContainer() {
205 return wrapped.isFolder();
206 }
207 /***
208 *@modified noel #890 made this public, so AladinAdapter interface can cast and get to this.
209 * once we drop aladinAdapter, can make this protected again.
210
211 */
212 public String getPath() {
213 return path;
214 }
215
216 public boolean equals(Object obj) {
217 IterationSixNode casted = (IterationSixNode)obj;
218 return this.getPath().equals(casted.getPath());
219 }
220 public String toString() {
221 return "Node (IterationSix) for " + (isFile() ? "file " : "folder ") + getPath();
222 }
223 }
224 /***
225 * Iteration 6 version of a container
226 * @author Noel Winstanley nw@jb.man.ac.uk 05-Nov-2004
227 *
228 */
229 protected class IterationSixContainer extends IterationSixNode implements Container {
230
231 /*** Construct a new IterationSixContainer
232 * @param wrapped
233 */
234 public IterationSixContainer(StoreFile wrapped) {
235 super(wrapped);
236 buildChildren();
237 }
238
239 private Map childMap;
240 /*** populate the children of the map - will recurse */
241 private final void buildChildren() {
242 StoreFile[] children = wrapped.listFiles();
243 childMap = new HashMap(children.length);
244 for (int i = 0; i < children.length; i++) {
245 StoreFile f = children[i];
246 logger.debug("found child:" + f.getName());
247 IterationSixNode n = f.isFile() ? (IterationSixNode)new IterationSixFile(f) : (IterationSixNode)new IterationSixContainer(f);
248 childMap.put(n.getName(),n);
249 }
250 }
251
252 public Collection getChildNodes() {
253 return Collections.unmodifiableCollection(childMap.values());
254 }
255
256 public Container addContainer(String name) throws TreeClientServiceException, TreeClientDuplicateException {
257 if (name == null || name.trim().length() == 0) {
258 throw new IllegalArgumentException("Cannot create unnamed container");
259 }
260 String targetPath = getPath() + "/" + name.trim();
261 logger.info("Will add container " + targetPath);
262 try {
263 if (client.getFile(targetPath) != null){
264 logger.info("duplicate of " + targetPath + " found");
265 throw new TreeClientDuplicateException(targetPath);
266 }
267 client.newFolder(targetPath);
268 IterationSixNode newFolder = findNewNode(targetPath);
269 childMap.put(newFolder.getName(),newFolder);
270 return (Container)newFolder;
271 } catch (IOException e) {
272 logger.error("addContainer(" + targetPath+")",e);
273 throw new TreeClientServiceException("addContainer(" + targetPath+")",e);
274 }
275 }
276
277 public File addFile(String name) throws TreeClientServiceException, TreeClientDuplicateException {
278 if (name == null || name.trim().length() == 0) {
279 throw new IllegalArgumentException("Cannot create unnamed file");
280 }
281 String targetPath = getPath() + "/" + name.trim();
282 logger.info("Will add file " + targetPath);
283 OutputStream os = null;
284 try {
285 if (client.getFile(targetPath) != null) {
286 logger.info("duplicate of " + targetPath + " found");
287 throw new TreeClientDuplicateException(targetPath);
288 }
289
290 os = client.putStream(targetPath,false);
291 IterationSixNode newFile = findNewNode(targetPath);
292 childMap.put(newFile.getName(),newFile);
293 return (File)newFile;
294 } catch (IOException e) {
295 logger.error("addFile(" + targetPath +")",e);
296 throw new TreeClientServiceException("addFile(" + targetPath + ")",e);
297 } finally {
298 if (os != null) {
299 try {
300 os.close();
301 }catch (IOException e) {
302 logger.warn("failed to close output stream",e);
303 }
304 }
305 }
306
307 }
308
309 /*** We've just created a new node. Now want to access the store file for that node, so it can be spliced into our tree.
310 *
311 * lots of problems here - see {@link org.astrogrid.store.adapter.aladin.integration.TestOddBehaviourOfStoreClient} for detals.
312 * <br>StoreClient.getFile(targetPath) won't return any information in the file object.
313 * <br>StoreClient.getFiles(targetPath) throws an exception if targeetPath contains a '/'
314 * only option is to re-query root, traverse returned tree using target path, and splice target node into existing tree. CRAP!
315 * @param targetPath the path to the newly created node
316 * @return the new storeFile.
317 * @throws TreeClientServiceException
318 * @todo fix StoreClient so that this can be done efficiently.
319 */
320 private IterationSixNode findNewNode(String targetPath) throws TreeClientServiceException {
321
322
323
324
325
326
327
328 try {
329 StringTokenizer tok = new StringTokenizer(targetPath,"/");
330 tok.nextToken();
331
332 IterationSixNode current = (IterationSixNode)getRoot();
333 while (tok.hasMoreTokens()) {
334 String name = tok.nextToken();
335 current = (IterationSixNode) ((IterationSixContainer)current).childMap.get(name);
336 }
337 return current;
338 } catch (TreeClientSecurityException e) {
339
340 logger.fatal("findNewNode - not logged in",e);
341 throw new IllegalStateException("find new Node - not logged in");
342 }
343 }
344 }
345
346
347
348 /***
349 * Iteration 6 implementation of a file.
350 * @author Noel Winstanley nw@jb.man.ac.uk 05-Nov-2004
351 *
352 */
353 protected class IterationSixFile extends IterationSixNode implements File {
354
355 /*** Construct a new IterationSixFile
356 * @param wrapped
357 */
358 public IterationSixFile(StoreFile wrapped) {
359 super(wrapped);
360 }
361
362 public String getMimeType() {
363 return wrapped.getMimeType();
364 }
365
366 public OutputStream getOutputStream() throws TreeClientServiceException {
367 try {
368 return client.putStream(this.getPath(),false);
369 } catch (IOException e) {
370 logger.error("Could not get output stream for " + this.getPath(),e);
371 throw new TreeClientServiceException("Could not get output stream for " + this.getPath(),e);
372 }
373 }
374
375
376 public InputStream getInputStream() throws TreeClientServiceException {
377 try {
378 return client.getStream(this.getPath());
379 } catch (IOException e) {
380 logger.error("Could not get input stream for " + this.getPath(),e);
381 throw new TreeClientServiceException("Could not get input stream for " + this.getPath(),e);
382 }
383
384 }
385 }
386
387
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419