Project

General

Profile

1 5298 jones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6 5299 jones
 *   '$Author: $'
7 5298 jones
 *     '$Date: 2009-06-13 15:28:13 +0300  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.dataone;
24
25 5319 jones
import java.io.File;
26
import java.io.FileNotFoundException;
27
import java.io.FileOutputStream;
28 5298 jones
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.sql.SQLException;
32
import java.util.Date;
33
import java.util.Enumeration;
34
import java.util.Hashtable;
35
import java.util.Timer;
36
37
import javax.servlet.ServletContext;
38
import javax.servlet.http.HttpServletRequest;
39
import javax.servlet.http.HttpServletResponse;
40
41 5319 jones
import org.apache.commons.io.IOUtils;
42
import org.apache.log4j.Logger;
43 5298 jones
import org.dataone.service.exceptions.IdentifierNotUnique;
44
import org.dataone.service.exceptions.InsufficientResources;
45
import org.dataone.service.exceptions.InvalidRequest;
46
import org.dataone.service.exceptions.InvalidSystemMetadata;
47
import org.dataone.service.exceptions.InvalidToken;
48
import org.dataone.service.exceptions.NotAuthorized;
49
import org.dataone.service.exceptions.NotFound;
50
import org.dataone.service.exceptions.NotImplemented;
51
import org.dataone.service.exceptions.ServiceFailure;
52
import org.dataone.service.exceptions.UnsupportedType;
53
import org.dataone.service.mn.MemberNodeCrud;
54
import org.dataone.service.types.AuthToken;
55
import org.dataone.service.types.Checksum;
56
import org.dataone.service.types.DescribeResponse;
57 5320 jones
import org.dataone.service.types.Identifier;
58 5298 jones
import org.dataone.service.types.LogRecordSet;
59
import org.dataone.service.types.SystemMetadata;
60
61
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
62
63 5319 jones
import edu.ucsb.nceas.metacat.AccessionNumberException;
64
import edu.ucsb.nceas.metacat.DocumentImpl;
65
import edu.ucsb.nceas.metacat.EventLog;
66 5298 jones
import edu.ucsb.nceas.metacat.IdentifierManager;
67
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
68
import edu.ucsb.nceas.metacat.McdbException;
69
import edu.ucsb.nceas.metacat.MetacatHandler;
70
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
71 5319 jones
import edu.ucsb.nceas.metacat.properties.PropertyService;
72
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
73 5298 jones
import edu.ucsb.nceas.metacat.service.SessionService;
74
import edu.ucsb.nceas.metacat.util.SessionData;
75
import edu.ucsb.nceas.utilities.ParseLSIDException;
76
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
77
78
/**
79
 *
80
 * Implements DataONE MemberNode CRUD API for Metacat.
81
 *
82
 * @author Matthew Jones
83
 */
84
public class CrudService implements MemberNodeCrud {
85
86
    private ServletContext servletContext;
87
    private HttpServletRequest request;
88
    private HttpServletResponse response;
89
90
//    private Logger logMetacat;
91
    private MetacatHandler handler;
92
//    private String username;
93
//    private String password;
94
//    private String sessionId;
95
//    private String[] groupNames;
96
    private Hashtable<String, String[]> params;
97 5319 jones
    Logger logMetacat = null;
98 5298 jones
99
    /**
100
     * Initializes new instance by setting servlet context,request and response.
101
     * TODO: remove dependency on Servlet infrastructure
102
     * TODO: Make this a real service, and make it a Singleton
103
     */
104
    public CrudService(ServletContext servletContext,
105
            HttpServletRequest request, HttpServletResponse response) {
106
        this.servletContext = servletContext;
107
        this.request = request;
108
        this.response = response;
109 5319 jones
        logMetacat = Logger.getLogger(CrudService.class);
110 5298 jones
111
        handler = new MetacatHandler(this.servletContext, new Timer());
112
        initParams();
113
//        loadSessionData();
114
    }
115
116
    /**
117
     *  copies request parameters to a Hashtable which is given as argument to
118
     *  native MetacatHandler functions
119
     */
120
    private void initParams() {
121
122
        String name = null;
123
        String[] value = null;
124
        params = new Hashtable<String, String[]>();
125
        Enumeration paramlist = request.getParameterNames();
126
        while (paramlist.hasMoreElements()) {
127
            name = (String) paramlist.nextElement();
128
            value = request.getParameterValues(name);
129
            params.put(name, value);
130
        }
131
    }
132
133
    /**
134
     *
135
     * Load user details of metacat session from the request
136
     *
137
     */
138
//    private void loadSessionData() {
139
//        SessionData sessionData = RequestUtil.getSessionData(request);
140
//
141
//        // TODO: validate the session before allowing these values to be set
142
//        username = sessionData.getUserName();
143
//        password = sessionData.getPassword();
144
//        groupNames = sessionData.getGroupNames();
145
//        sessionId = sessionData.getId();
146
//
147
//        if (username == null) {
148
//            username = "public";
149
//        }
150
//    }
151
152
    /*
153
     * Look up the information on the session using the token provided in
154
     * the AuthToken.  The Session should have all relevant user information.
155
     * If the session has expired or is invalid, the 'public' session will
156
     * be returned, giving the user anonymous access.
157
     */
158
    private static SessionData getSessionData(AuthToken token) {
159
        SessionData sessionData = null;
160 5299 jones
        String sessionId = "PUBLIC";
161
        if (token != null) {
162
            sessionId = token.getToken();
163
        }
164 5298 jones
165
        // if the session id is registered in SessionService, get the
166
        // SessionData for it. Otherwise, use the public session.
167
        if (SessionService.isSessionRegistered(sessionId)) {
168
            sessionData = SessionService.getRegisteredSession(sessionId);
169
        } else {
170
            sessionData = SessionService.getPublicSession();
171
        }
172
173
        return sessionData;
174
    }
175
176 5320 jones
    public Identifier create(AuthToken token, Identifier guid,
177 5298 jones
            InputStream object, SystemMetadata sysmeta) throws InvalidToken,
178
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType,
179
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
180 5319 jones
181
        logMetacat.debug("Starting CrudService.create()...");
182
183
        // TODO: authenticate & get user info
184
        String username = "GARBAGETOBEREPLACED";
185
        String[] groups = null;
186
187
        // verify that guid == SystemMetadata.getIdentifier()
188 5320 jones
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
189 5319 jones
//        if (!guid.getIdentifier().equals(sysmeta.getIdentifier().getIdentifier())) {
190
//            throw new InvalidSystemMetadata(1180,
191
//                "GUID in method call does not match GUID in system metadata.");
192
//        }
193
194
        logMetacat.debug("Checking if identifier exists...");
195
        // Check that the identifier does not already exist
196
        IdentifierManager im = IdentifierManager.getInstance();
197 5320 jones
        if (im.identifierExists(guid.getValue())) {
198 5319 jones
            throw new IdentifierNotUnique(1120,
199
                "GUID is already in use by an existing object.");
200
        }
201
202
        // generate guid/localId pair for object
203
        logMetacat.debug("Generating a guid/localId mapping");
204 5320 jones
        String localId = im.generateLocalId(guid.getValue(), 1);
205 5319 jones
206
        // TODO: generate guid/localId pair for sysmeta
207
        // TODO: update system metadata fields
208
        // TODO: insert system metadata to metacat (probably at end)
209
210
        // TODO: Check if we are handling metadata or data
211
212
        // TODO: CASE METADATA:
213
        // Setup and call handleInsertOrUpdate()
214
215
        // TODO: DEFAULT CASE: DATA
216
        try {
217
            logMetacat.debug("Case DATA: starting to write to disk.");
218
            if (DocumentImpl.getDataFileLockGrant(localId)) {
219
220
                // Save the data file to disk using "localId" as the name
221
                try {
222
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
223
224
                    File dataDirectory = new File(datafilepath);
225
                    dataDirectory.mkdirs();
226
227
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
228
229
                    // TODO: Check that the file size matches SystemMetadata
230
                    //                        long size = newFile.length();
231
                    //                        if (size == 0) {
232
                    //                            throw new IOException("Uploaded file is 0 bytes!");
233
                    //                        }
234
235
                    //register the file in the database (which generates an exception
236
                    // if the docid is not acceptable or other untoward things happen
237
                    try {
238
                        logMetacat.debug("Registering document...");
239
                        DocumentImpl.registerDocument(localId, "BIN", localId,
240
                                username, groups);
241
                        logMetacat.debug("Registrtion step completed.");
242
                    } catch (SQLException e) {
243
                        //newFile.delete();
244
                        logMetacat.debug("SQLE: " + e.getMessage());
245
                        e.printStackTrace(System.out);
246
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
247
                    } catch (AccessionNumberException e) {
248
                        //newFile.delete();
249
                        logMetacat.debug("ANE: " + e.getMessage());
250
                        e.printStackTrace(System.out);
251
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
252
                    } catch (Exception e) {
253
                        //newFile.delete();
254
                        logMetacat.debug("Exception: " + e.getMessage());
255
                        e.printStackTrace(System.out);
256
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
257
                    }
258
259
                    logMetacat.debug("Logging the creation event.");
260
                    EventLog.getInstance().log(request.getRemoteAddr(),
261
                            username, localId, "create");
262
263
                    // Force replication this data file
264
                    // To data file, "insert" and update is same
265
                    // The fourth parameter is null. Because it is notification
266
                    // server and this method is in MetaCatServerlet. It is
267
                    // original command, not get force replication info from
268
                    // another metacat
269
                    // TODO: note that GUID mapping is not being replicated
270
                    logMetacat.debug("Scheduling replication.");
271
                    ForceReplicationHandler frh = new ForceReplicationHandler(
272
                            localId, "insert", false, null);
273
274
                } catch (PropertyNotFoundException e) {
275
                    throw new ServiceFailure(1190, "Could not lock file for writing:" + e.getMessage());
276
                }
277
278
            }
279
        } catch (Exception e) {
280
            // Could not get a lock on the document, so we can not update the file now
281
            throw new ServiceFailure(1190, "Failed to lock file: " + e.getMessage());
282
        }
283
284
        logMetacat.debug("Returning from CrudService.create()");
285
        return guid;
286 5298 jones
    }
287
288 5320 jones
    public Identifier delete(AuthToken token, Identifier guid)
289 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
290
            NotImplemented {
291
        throw new NotImplemented(1000, "This method not yet implemented.");
292
    }
293
294 5320 jones
    public DescribeResponse describe(AuthToken token, Identifier guid)
295 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
296
            NotImplemented {
297
        throw new NotImplemented(1000, "This method not yet implemented.");
298
    }
299
300 5320 jones
    public InputStream get(AuthToken token, Identifier guid)
301 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
302
            NotImplemented {
303
304
        // Retrieve the session information from the AuthToken
305
        // If the session is expired, then the user is 'public'
306
        final SessionData sessionData = getSessionData(token);
307
308
        // Look up the localId for this global identifier
309
        IdentifierManager im = IdentifierManager.getInstance();
310
        try {
311 5320 jones
            final String localId = im.getLocalId(guid.getValue());
312 5298 jones
313
            final InputStreamFromOutputStream<String> objectStream =
314
                new InputStreamFromOutputStream<String>() {
315
316
                @Override
317
                public String produce(final OutputStream dataSink) throws Exception {
318
319
                    try {
320
                        handler.readFromMetacat(request.getRemoteAddr(), null,
321
                                dataSink, localId, "xml",
322
                                sessionData.getUserName(),
323
                                sessionData.getGroupNames(), true, params);
324
                    } catch (PropertyNotFoundException e) {
325
                        throw new ServiceFailure(1030, e.getMessage());
326
                    } catch (ClassNotFoundException e) {
327
                        throw new ServiceFailure(1030, e.getMessage());
328
                    } catch (IOException e) {
329
                        throw new ServiceFailure(1030, e.getMessage());
330
                    } catch (SQLException e) {
331
                        throw new ServiceFailure(1030, e.getMessage());
332
                    } catch (McdbException e) {
333
                        throw new ServiceFailure(1030, e.getMessage());
334
                    } catch (ParseLSIDException e) {
335
                        throw new NotFound(1020, e.getMessage());
336
                    } catch (InsufficientKarmaException e) {
337
                        throw new NotAuthorized(1000, "Not authorized for get().");
338
                    }
339
340
                    return "Completed";
341
                }
342
            };
343
            return objectStream;
344
345
        } catch (McdbDocNotFoundException e) {
346
            throw new NotFound(1020, e.getMessage());
347
        }
348
349
/*
350
 *      Alternative approach that uses Piped streams, but requires thread handling
351
 *      which makes exception handling difficult (because exceptions fall off the
352
 *      calling thread, terminating the thread.
353
 *
354
        // Look up the localId for this global identifier
355
        IdentifierManager im = IdentifierManager.getInstance();
356
        try {
357
            final String localId = im.getLocalId(guid.getIdentifier());
358
359
            // Now use that localId to read the object and return it
360
            params.put("docid", new String[] { localId });
361
            final PipedInputStream in = new PipedInputStream();
362
            new Thread(
363
                    new Runnable() {
364
                        public void run() {
365
                            try {
366
                                PipedOutputStream out = new PipedOutputStream(in);
367
                                handler.readFromMetacat(request.getRemoteAddr(), null,
368
                                        out, localId, "xml",
369
                                        username, groupNames, true, params);
370
                            } catch (PropertyNotFoundException e) {
371
                                throw new ServiceFailure(1030, e.getMessage());
372
                            } catch (ClassNotFoundException e) {
373
                                throw new ServiceFailure(1030, e.getMessage());
374
                            } catch (IOException e) {
375
                                throw new ServiceFailure(1030, e.getMessage());
376
                            } catch (SQLException e) {
377
                                throw new ServiceFailure(1030, e.getMessage());
378
                            } catch (McdbException e) {
379
                                throw new ServiceFailure(1030, e.getMessage());
380
                            } catch (ParseLSIDException e) {
381
                                throw new NotFound(1020, e.getMessage());
382
                            } catch (InsufficientKarmaException e) {
383
                                throw new NotAuthorized(1000, "Not authorized for get().");
384
                            }
385
                        }
386
                    }
387
            ).start();
388
            return in;
389
        } catch (McdbDocNotFoundException e) {
390
            throw new NotFound(1020, e.getMessage());
391
        }
392
*/
393
    }
394
395 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid)
396 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
397
            InvalidRequest, NotImplemented {
398
        throw new NotImplemented(1000, "This method not yet implemented.");
399
    }
400
401 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid,
402 5298 jones
            String checksumAlgorithm) throws InvalidToken, ServiceFailure,
403
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
404
        throw new NotImplemented(1000, "This method not yet implemented.");
405
    }
406
407
    public LogRecordSet getLogRecords(AuthToken token, Date fromDate, Date toDate)
408
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
409
            NotImplemented {
410
        throw new NotImplemented(1000, "This method not yet implemented.");
411
    }
412
413 5320 jones
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
414 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
415
            InvalidRequest, NotImplemented {
416 5319 jones
417
        // Look up ID of system metadata based on guid
418
            // Initially from document, later from entry in table?
419
420
        // Read system metadata from disk and create SystemMetadata object
421
            // Follows same implementation plan as get()
422
423
        // return it
424 5298 jones
        throw new NotImplemented(1000, "This method not yet implemented.");
425
    }
426
427 5320 jones
    public Identifier update(AuthToken token, Identifier guid,
428
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta)
429 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
430
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata,
431
            NotImplemented {
432
        throw new NotImplemented(1000, "This method not yet implemented.");
433
    }
434
435 5319 jones
    private File writeStreamToFile(File dir, String fileName, InputStream data)
436
        throws ServiceFailure {
437
//        logMetacat.debug("***********************************************");
438
//        logMetacat.debug("** FILE TEST FROM CS                         **");
439
//        logMetacat.debug("***********************************************");
440
441
        File newFile = new File(dir, fileName);
442
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
443
444
        try {
445
            if (newFile.createNewFile()) {
446
//                logMetacat.debug("File doesn't yet exist, so write to it.");
447
//                logMetacat.debug("Creating file stream...");
448
449
                // write data stream to desired file
450
                OutputStream os = new FileOutputStream(newFile);
451
//                logMetacat.debug("Copying file stream...");
452
                long length = IOUtils.copyLarge(data, os);
453
                os.flush();
454
//                logMetacat.debug("Closing file stream...");
455
                os.close();
456
//                logMetacat.debug("Done with file stream...");
457
            } else {
458
                logMetacat.debug("File creation failed, or file already exists.");
459
                throw new ServiceFailure(1190, "File already exists: " + fileName);
460
            }
461
        } catch (FileNotFoundException e) {
462
            logMetacat.debug("FNF: " + e.getMessage());
463
            throw new ServiceFailure(1190, "File not found: " + fileName + " "
464
                    + e.getMessage());
465
        } catch (IOException e) {
466
            logMetacat.debug("IOE: " + e.getMessage());
467
            throw new ServiceFailure(1190, "File was not written: " + fileName
468
                    + " " + e.getMessage());
469
        }
470
471
//        logMetacat.debug("***********************************************");
472
//        logMetacat.debug("** END FILE TEST                             **");
473
//        logMetacat.debug("***********************************************");
474
475
        return newFile;
476
    }
477 5298 jones
}