Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements utility methods for a metadata catalog
4
 *  Copyright: 2009 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Michael Daigle
7
 *
8
 *   '$Author: daigle $'
9
 *     '$Date: 2009-08-04 14:32:58 -0700 (Tue, 04 Aug 2009) $'
10
 * '$Revision: 5015 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.util;
28

    
29
import java.io.PrintWriter;
30
import java.sql.SQLException;
31
import java.util.Calendar;
32
import java.util.Date;
33
import java.util.GregorianCalendar;
34
import java.util.Hashtable;
35
import java.util.SimpleTimeZone;
36
import java.util.Stack;
37
import java.util.TimeZone;
38
import java.util.Vector;
39

    
40
import javax.servlet.http.HttpServletRequest;
41
import javax.servlet.http.HttpServletResponse;
42

    
43
import org.apache.log4j.Logger;
44

    
45
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
46
import edu.ucsb.nceas.metacat.DBSAXHandler;
47
import edu.ucsb.nceas.metacat.DBUtil;
48
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
49
import edu.ucsb.nceas.metacat.McdbException;
50
import edu.ucsb.nceas.metacat.NodeRecord;
51
import edu.ucsb.nceas.metacat.PermissionController;
52
import edu.ucsb.nceas.metacat.properties.PropertyService;
53
import edu.ucsb.nceas.metacat.service.SessionService;
54
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
55
import edu.ucsb.nceas.metacat.util.SessionData;
56
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
57
import edu.ucsb.nceas.utilities.ParseLSIDException;
58
import edu.ucsb.nceas.utilities.LSIDUtil;
59

    
60
/**
61
 * A suite of utility classes for the metadata catalog server
62
 */
63
public class DocumentUtil
64
{
65
	
66
    private static int documentIdCounter = 0;
67

    
68
	public static AbstractDatabase dbAdapter;
69
    
70
    private static Logger logMetacat = Logger.getLogger(DocumentUtil.class);
71
    private static char separator = '.';
72
    private static String prefix = "autogen";
73
    
74
    static {
75
        try {
76
        	separator = PropertyService.getProperty("document.accNumSeparator").charAt(0);    	
77
        } catch (PropertyNotFoundException pnfe) {
78
        	logMetacat.error("DocumentUtil() - Could not retrieve accession number separator. " 
79
        			+ "Separator set to '.' : " + pnfe.getMessage());
80
        }
81
        try {
82
            prefix = PropertyService.getProperty("document.accNumPrefix");      
83
        } catch (PropertyNotFoundException pnfe) {
84
            logMetacat.error("DocumentUtil() - Could not retrieve accession number prefix. " 
85
                    + "Prefix set to " + prefix + ": " + pnfe.getMessage());
86
        }
87
    }
88

    
89
    /**
90
     * Get docid from online/url string
91
     */
92
    public static String getDocIdWithRevFromOnlineURL(String url)
93
    {
94
        String docid = null;
95
        String DOCID = "docid";
96
        boolean find = false;
97
        char limited = '&';
98
        int count = 0; //keep track how many & was found
99
        Vector list = new Vector();// keep index number for &
100
        if (url == null) {
101
            logMetacat.debug("DocumentUtil.getDocIdWithRevFromOnlineURL - url is null and null will be returned");
102
            return docid;
103
        }
104
        // the first element in list is 0
105
        list.add(new Integer(0));
106
        for (int i = 0; i < url.length(); i++) {
107
            if (url.charAt(i) == limited) {
108
                // count plus 1
109
                count++;
110
                list.add(new Integer(i));
111
                // get substring beween two &
112
                String str = url.substring(
113
                        ((Integer) list.elementAt(count - 1)).intValue(), i);
114
                logMetacat.debug("DocumentUtil.getDocIdWithRevFromOnlineURL - substring between two & is: " + str);
115
                //if the subString contains docid, we got it
116
                if (str.indexOf(DOCID) != -1) {
117
                    //get index of '="
118
                    int start = getIndexForGivenChar(str, '=') + 1;
119
                    int end = str.length();
120
                    docid = str.substring(start, end);
121
                    find = true;
122
                }//if
123
            }//if
124
        }//for
125
        //if not find, we need check the subtring between the index of last &
126
        // and
127
        // the end of string
128
        if (!find) {
129
            logMetacat.debug("DocumentUtil.getDocIdWithRevFromOnlineURL - Checking the last substring");
130
            String str = url.substring(((Integer) list.elementAt(count))
131
                    .intValue() + 1, url.length());
132
            logMetacat.debug("DocumentUtil.getDocIdWithRevFromOnlineURL - Last substring is: " + str);
133
            if (str.indexOf(DOCID) != -1) {
134
                //get index of '="
135
                int start = getIndexForGivenChar(str, '=') + 1;
136
                int end = str.length();
137
                docid = str.substring(start, end);
138
                find = true;
139
            }//if
140
        }//if
141
        logMetacat.debug("DocumentUtil.getDocIdWithRevFromOnlineURL - The docid from online url is:" + docid);
142
        return docid.trim();
143
    }
144

    
145

    
146
    /**
147
     * Eocgorid identifier will look like: ecogrid://knb/tao.1.1
148
     * The AccessionNumber tao.1.1 will be returned. If the given doesn't
149
     * contains ecogrid, null will be returned.
150
     * @param identifier String
151
     * @return String
152
     */
153
    public static String getAccessionNumberFromEcogridIdentifier(String identifier)
154
    {
155
      String accessionNumber = null;
156
      if (identifier != null && identifier.startsWith(DBSAXHandler.ECOGRID))
157
      {
158
        // find the last "/" in identifier
159
        int indexOfLastSlash = identifier.lastIndexOf("/");
160
        int start = indexOfLastSlash+1;
161
        int end   = identifier.length();
162
        accessionNumber = identifier.substring(start, end);
163
      }
164
      logMetacat.info("DocumentUtil.getAccessionNumberFromEcogridIdentifier - The accession number from url is " +
165
                                 accessionNumber);
166
      return accessionNumber;
167
    }
168

    
169
    private static int getIndexForGivenChar(String str, char character)
170
    {
171
        int index = -1;
172
        // make sure str is not null
173
        if (str == null) {
174
            logMetacat.debug("DocumentUtil.getIndexForGivenChar - " +
175
                    "The given str is null and -1 will be returned");
176
            return index;
177
        }
178
        // got though the string
179
        for (int i = 0; i < str.length(); i++) {
180
            // find the first one then break the loop
181
            if (str.charAt(i) == character) {
182
                index = i;
183
                break;
184
            }//if
185
        }//for
186
        logMetacat.info("DocumentUtil.getIndexForGivenChar - the index for char " + 
187
        		character + " is: " + index);
188
        return index;
189
    }
190

    
191
    /**
192
     * Utility method to get docid from a given string
193
     *
194
     * @param string, the given string should be these two format: 1) str1.str2
195
     *            in this case docid= str1.str2 2) str1.str2.str3, in this case
196
     *            docid =str1.str2
197
     * @param the sperator char
198
     */
199
    public static String getDocIdFromString(String str)
200
    {
201
        String docId = null;
202
        if (str == null) {
203
            logMetacat.debug(
204
                    "DocumentUtil.getDocIdFromString - The given str is null and null will be returned"
205
                            + " in getDocIdfromString");
206
            return docId;
207
        } //make sure docid is not null
208
        int dotNumber = 0;//count how many dots in given string
209
        int indexOfLastDot = 0;
210

    
211
        for (int i = 0; i < str.length(); i++) {
212
            if (str.charAt(i) == separator) {
213
                dotNumber++;//count how many dots
214
                indexOfLastDot = i;//keep the last dot postion
215
            }
216
        }//for
217

    
218
        //The string formatt is wrong, because it has more than two or less
219
        // than
220
        //one seperator
221
        if (dotNumber > 2 || dotNumber < 1) {
222
            docId = null;
223
        } else if (dotNumber == 2) //the case for str1.str2.str3
224
        {
225
            docId = str.substring(0, indexOfLastDot);
226
        } else if (dotNumber == 1) //the case for str1.str2
227
        {
228
            docId = str;
229
        }
230

    
231
        return docId;
232
    }//getDocIdFromString
233

    
234
    /**
235
     * Utility method to get version number from a given string
236
     *
237
     * @param string, the given string should be these two format: 1)
238
     *            str1.str2(no version) version =-1; 2) str1.str2.str3, in this
239
     *            case version = str3; 3) other, vresion =-2
240
     */
241
    public static int getVersionFromString(String str)
242
            throws NumberFormatException
243
    {
244
        int version = -1;
245
        String versionString = null;
246
        int dotNumber = 0;//count how many dots in given string
247
        int indexOfLastDot = 0;
248

    
249
        for (int i = 0; i < str.length(); i++) {
250
            if (str.charAt(i) == separator) {
251
                dotNumber++;//count how many dots
252
                indexOfLastDot = i;//keep the last dot postion
253
            }
254
        }//for
255

    
256
        //The string formatt is wrong, because it has more than two or less
257
        // than
258
        //one seperator
259
        if (dotNumber > 2 || dotNumber < 1) {
260
            version = -2;
261
        } else if (dotNumber == 2 && (indexOfLastDot != (str.length() - 1)))
262
        //the case for str1.str2.str3
263
        {
264
            versionString = str.substring((indexOfLastDot + 1), str.length());
265
            version = Integer.parseInt(versionString);
266
        } else if (dotNumber == 1) //the case for str1.str2
267
        {
268
            version = -1;
269
        }
270

    
271
        return version;
272
    }//getVersionFromString
273

    
274
    /**
275
     * Utility method to get version string from a given string
276
     *
277
     * @param string, the given string should be these two format: 1)
278
     *            str1.str2(no version) version=null; 2) str1.str2.str3, in
279
     *            this case version = str3; 3) other, vresion =null;
280
     */
281
    public static String getRevisionStringFromString(String str)
282
            throws NumberFormatException
283
    {
284
        // String to store the version
285
        String versionString = null;
286
        int dotNumber = 0;//count how many dots in given string
287
        int indexOfLastDot = 0;
288

    
289
        for (int i = 0; i < str.length(); i++) {
290
            if (str.charAt(i) == separator) {
291
                dotNumber++;//count how many dots
292
                indexOfLastDot = i;//keep the last dot postion
293
            }
294
        }//for
295

    
296
        //The string formatt is wrong, because it has more than two or less
297
        // than
298
        //one seperator
299
        if (dotNumber > 2 || dotNumber < 1) {
300
            versionString = null;
301
        } else if (dotNumber == 2 && (indexOfLastDot != (str.length() - 1))) {
302
            //the case for str1.str2.str3
303
            // indexOfLastDot != (str.length() -1) means get rid of str1.str2.
304
            versionString = str.substring((indexOfLastDot + 1), str.length());
305
        } else if (dotNumber == 1) //the case for str1.str2 or str1.str2.
306
        {
307
            versionString = null;
308
        }
309

    
310
        return versionString;
311
    }//getVersionFromString
312
    
313
    /**
314
     * If the given docid only have one seperter, we need
315
     * append rev for it. The rev come from xml_documents
316
     */
317
    public static String appendRev(String docid) 
318
      throws PropertyNotFoundException, SQLException, McdbDocNotFoundException {
319
        String newAccNum = null;
320
        String separator = PropertyService.getProperty("document.accNumSeparator");
321
        int firstIndex = docid.indexOf(separator);
322
        int lastIndex = docid.lastIndexOf(separator);
323
        if (firstIndex == lastIndex) {
324
            
325
            //only one seperater
326
            int rev = DBUtil.getLatestRevisionInDocumentTable(docid);
327
            if (rev == -1) {
328
                throw new McdbDocNotFoundException("the requested docid '"
329
                        + docid+ "' does not exist");
330
            } else {
331
                newAccNum = docid+ separator+ rev;
332
            }
333
        } else {
334
            // in other suituation we don't change the docid
335
            newAccNum = docid;
336
        }
337
        //logMetacat.debug("The docid will be read is "+newAccNum);
338
        return newAccNum;
339
    }
340

    
341
    /**
342
     * This method will get docid from an AccessionNumber. There is no
343
     * assumption the accessnumber will be str1.str2.str3. It can be more. So
344
     * we think the docid will be get rid of last part
345
     */
346
    public static String getDocIdFromAccessionNumber(String accessionNumber)
347
    {
348
        String docid = null;
349
        if (accessionNumber == null) { return docid; }
350
        int indexOfLastSeperator = accessionNumber.lastIndexOf(separator);
351
        docid = accessionNumber.substring(0, indexOfLastSeperator);
352
        logMetacat.debug("DocumentUtil.getDocIdFromAccessionNumber - after parsing accession number, docid is "
353
                + docid);
354
        return docid;
355
    }
356

    
357
    /**
358
     * This method will get inline data id without the revision number.
359
     * So if inlineData.1.2 is passed as input, inlineData.2 is returned.
360
     */
361
    public static String getInlineDataIdWithoutRev(String accessionNumber)
362
    {
363
        String docid = null;
364
        if (accessionNumber == null) { return docid; }
365
        int indexOfLastSeperator = accessionNumber.lastIndexOf(separator);
366
        String version = accessionNumber.substring(indexOfLastSeperator,
367
                                                   accessionNumber.length());
368
        accessionNumber = accessionNumber.substring(0, indexOfLastSeperator);
369
        indexOfLastSeperator = accessionNumber.lastIndexOf(separator);
370
        docid = accessionNumber.substring(0, indexOfLastSeperator) + version;
371
        logMetacat.debug("DocumentUtil.getInlineDataIdWithoutRev - after parsing accessionnumber, docid is "
372
                                 + docid);
373

    
374
        return docid;
375
    }
376

    
377
    /**
378
     * This method will call both getDocIdFromString and
379
     * getDocIdFromAccessionNumber. So first, if the string looks str1.str2,
380
     * the docid will be str1.str2. If the string is str1.str2.str3, the docid
381
     * will be str1.str2. If the string is str1.str2.str3.str4 or more, the
382
     * docid will be str1.str2.str3. If the string look like str1, null will be
383
     * returned
384
     *
385
     */
386
    public static String getSmartDocId(String str)
387
    {
388
        String docid = null;
389
        //call geDocIdFromString first.
390
        docid = getDocIdFromString(str);
391
        // If docid is null, try to call getDocIdFromAccessionNumber
392
        // it will handle the seperator more than2
393
        if (docid == null) {
394
            docid = getDocIdFromAccessionNumber(str);
395
        }
396
        logMetacat.debug("DocumentUtil.getSmartDocId - The docid get from smart docid getor is "
397
                + docid);
398
        return docid;
399
    }
400

    
401
    /**
402
     * This method will get revision from an AccessionNumber. There is no
403
     * assumption the accessnumber will be str1.str2.str3. It can be more. So
404
     * we think the docid will be get rid of last part
405
     */
406
    public static int getRevisionFromAccessionNumber(String accessionNumber)
407
            throws NumberFormatException
408
    {
409
        String rev = null;
410
        int revNumber = -1;
411
        if (accessionNumber == null) { return revNumber; }
412
        int indexOfLastSeperator = accessionNumber.lastIndexOf(separator);
413
        rev = accessionNumber.substring(indexOfLastSeperator + 1,
414
                accessionNumber.length());
415
        revNumber = Integer.parseInt(rev);
416
        logMetacat.debug("DocumentUtil.getRevisionFromAccessionNumber - after parsing accessionnumber, rev is "
417
                + revNumber);
418
        return revNumber;
419
    }
420

    
421
    /**
422
     * Method to get docidwithrev from eml2 inline data id The eml inline data
423
     * id would look like eml.200.2.3
424
     */
425
    public static String getDocIdFromInlineDataID(String inlineDataID)
426
    {
427
        String docidWithoutRev = null;
428
        if (inlineDataID == null) { return docidWithoutRev; }
429
        char charSeperator = separator;
430
        int targetNumberOfSeperator = 3;// we want to know his index
431
        int numberOfSeperator = 0;
432
        for (int i = 0; i < inlineDataID.length(); i++) {
433
            // meet seperator, increase number of seperator
434
            if (inlineDataID.charAt(i) == charSeperator) {
435
                numberOfSeperator++;
436
            }
437
            // if number of seperator reach the target one, record the index(i)
438
            // and get substring and terminate the loop
439
            if (numberOfSeperator == targetNumberOfSeperator) {
440
                docidWithoutRev = inlineDataID.substring(0, i);
441
                break;
442
            }
443
        }
444

    
445
        logMetacat.debug("DocumentUtil.getDocIdWithoutRevFromInlineDataID - Docid without rev from inlinedata id: "
446
                + docidWithoutRev);
447
        return docidWithoutRev;
448

    
449
    }
450
    
451
    /**
452
     * Revise stack change a stack to opposite order
453
     */
454
    public static Stack<NodeRecord> reviseStack(Stack<NodeRecord> stack)
455
    {
456
        Stack result = new Stack();
457
        // make sure the parameter is correct
458
        if (stack == null || stack.isEmpty()) {
459
            result = stack;
460
            return result;
461
        }
462

    
463
        while (!stack.isEmpty()) {
464
            Object obj = stack.pop();
465
            result.push(obj);
466
        }
467
        return result;
468
    }
469
    
470
    public static void isAuthorized(PrintWriter out, Hashtable<String,String[]> params, 
471
    		HttpServletRequest request, HttpServletResponse response) throws MetacatUtilException {
472
    	
473
    	String resourceLsid;
474
    	String[] resourceLsids = params.get("resourceLsid");
475
    	if (resourceLsids == null) {
476
    		throw new MetacatUtilException("DocumentUtil.isAuthorized - " + 
477
    				"resourceLsid parameter cannot be null.");
478
    	}
479
    	resourceLsid = resourceLsids[0];
480
    	 
481
    	String permission;
482
    	String[] permissions = params.get("permission");
483
    	if (permissions == null) {
484
    		throw new MetacatUtilException("DocumentUtil.isAuthorized - " + 
485
    				"permission parameter cannot be null.");
486
    	}
487
    	permission = permissions[0];
488
    	
489
    	String sessionId;
490
    	String[] sessionIds = params.get("sessionId");
491
    	if (sessionIds == null) {
492
    		throw new MetacatUtilException("DocumentUtil.isAuthorized - " + 
493
    				"sessionId parameter cannot be null.");
494
    	}
495
    	sessionId = sessionIds[0];
496
    	
497
    	String isAuthorized = "false";
498
    	String message = "";
499
    	
500
    	String result = "<resourceAuthorization>";
501
    	result += "<resourceId>" + resourceLsid + "</resourceId>"; 
502
    	result += "<permission>" + permission + "</permission>";
503
    	result += "<sessionId>" + sessionId + "</sessionId>";
504

    
505
    	if (!SessionService.getInstance().isSessionRegistered(sessionId)) {
506
    		message = "Session is not logged in";
507
    	} else {
508
    		SessionData sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
509
    		
510
    		String docId = null;
511
    		try {
512
    			docId = LSIDUtil.getDocId(resourceLsid, true);
513
    			PermissionController pc = new PermissionController(docId);   
514
    			if (pc.hasPermission(sessionData.getUserName(), sessionData.getGroupNames(), permission)) {
515
    				isAuthorized = "true";
516
    				message = " docid: " + docId + " is authorized for session";
517
    			}
518
    		} catch (ParseLSIDException ple) {
519
    			message = "unparseable resource lsid: " + ple.getMessage();
520
    		} catch (McdbException me) {
521
    			message = "could not create permission controller for docid: " + docId + " : " + me.getMessage();
522
    		} catch (SQLException sqle) {
523
    			message = "SQL error getting permissions for docid: " + docId + " : " + sqle.getMessage();
524
    		}
525
    	}
526
    	
527
    	result += "<isAuthorized>" + isAuthorized + "</isAuthorized>";
528
    	result += "<message>" + message + "</message>";
529
    	result += "</resourceAuthorization>";
530
    	
531
    	out.write(result);
532
    }
533
    
534
    /**
535
     * Create a unique docid for use in inserts and updates using the default
536
     * prefix from the document.accNumPrefix property. Does not include the 
537
     * 'revision' part of the id if revision is '0', otherwise sets the 
538
     * revision number to 'revision'.
539
     * 
540
     * @param idPrefix the prefix to be used to construct the scope portion of the docid
541
     * @param revision the integer revision to use for this docid
542
     * @return a String docid based on the current date and time
543
     */
544
    public static String generateDocumentId(int revision) {
545
        return generateDocumentId(prefix, revision);
546
    }
547
    
548
    /**
549
     * Create a unique docid for use in inserts and updates using the prefix
550
     * that is provided. Does not include the 'revision' part of the id if 
551
     * revision is '0', otherwise sets the revision number to 'revision'.
552
     * 
553
     * @param idPrefix the prefix to be used to construct the scope portion of the docid
554
     * @param revision the integer revision to use for this docid
555
     * @return a String docid based on the current date and time
556
     */
557
    public static String generateDocumentId(String idPrefix, int revision)
558
    {
559
        StringBuffer docid = new StringBuffer(idPrefix);
560
        docid.append(".");
561

    
562
        // Create a calendar to get the date formatted properly
563
        String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
564
        SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]);
565
        pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
566
        pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
567
        Calendar calendar = new GregorianCalendar(pdt);
568
        Date trialTime = new Date();
569
        calendar.setTime(trialTime);
570
        // using yyyymmddhhmmssmmm by convention (zero padding to preserve places)
571
        // will help with looking at logs and especially database tables.
572
        // for each millisecond we can support up to 99 before resetting to 0
573
        // NOTE: if you make it larger, docid is too big for a Long 
574
        if (documentIdCounter > 100) {
575
        	documentIdCounter = 0;
576
        }
577
        docid.append(String.format("%04d%02d%02d%02d%02d%02d%03d%02d",
578
        calendar.get(Calendar.YEAR),
579
        calendar.get(Calendar.MONTH) + 1,  // adjust 0-11 range to 1-12
580
        calendar.get(Calendar.DAY_OF_MONTH),
581
        calendar.get(Calendar.HOUR_OF_DAY),
582
        calendar.get(Calendar.MINUTE),
583
        calendar.get(Calendar.SECOND),
584
        calendar.get(Calendar.MILLISECOND),
585
        documentIdCounter++)
586
        );
587
        if (revision > 0) {
588
            docid.append(".").append(revision);
589
        }
590
        return docid.toString();
591
    }
592
}
(4-4/16)