Project

General

Profile

1
/**
2
 * Copyright 2006 OCLC Online Computer Library Center Licensed under the Apache
3
 * License, Version 2.0 (the "License"); you may not use this file except in
4
 * compliance with the License. You may obtain a copy of the License at
5
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
6
 * agreed to in writing, software distributed under the License is distributed on
7
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
8
 * or implied. See the License for the specific language governing permissions and
9
 * limitations under the License.
10
 */
11
package edu.ucsb.nceas.metacat.oaipmh.provider.server;
12

    
13
import java.io.FileInputStream;
14
import java.io.FileNotFoundException;
15
import java.io.IOException;
16
import java.io.InputStream;
17
import java.io.OutputStreamWriter;
18
import java.io.Writer;
19
import java.net.SocketException;
20
import java.util.Date;
21
import java.util.Enumeration;
22
import java.util.HashMap;
23
import java.util.Properties;
24
import java.util.zip.DeflaterOutputStream;
25
import java.util.zip.GZIPOutputStream;
26

    
27
import javax.servlet.ServletConfig;
28
import javax.servlet.ServletContext;
29
import javax.servlet.ServletException;
30
import javax.servlet.http.HttpServlet;
31
import javax.servlet.http.HttpServletRequest;
32
import javax.servlet.http.HttpServletResponse;
33

    
34
import javax.xml.transform.Transformer;
35
import javax.xml.transform.TransformerException;
36
import javax.xml.transform.TransformerFactory;
37
import javax.xml.transform.stream.StreamSource;
38

    
39
import org.apache.commons.io.IOUtils;
40
import org.apache.log4j.Logger;
41
import org.apache.log4j.PropertyConfigurator;
42

    
43
import edu.ucsb.nceas.metacat.oaipmh.provider.server.crosswalk.Eml2oai_dc;
44
import edu.ucsb.nceas.metacat.properties.PropertyService;
45
import edu.ucsb.nceas.metacat.shared.ServiceException;
46

    
47
import ORG.oclc.oai.server.catalog.AbstractCatalog;
48
import ORG.oclc.oai.server.verb.BadVerb;
49
import ORG.oclc.oai.server.verb.GetRecord;
50
import ORG.oclc.oai.server.verb.Identify;
51
import ORG.oclc.oai.server.verb.ListIdentifiers;
52
import ORG.oclc.oai.server.verb.ListMetadataFormats;
53
import ORG.oclc.oai.server.verb.ListRecords;
54
import ORG.oclc.oai.server.verb.ListSets;
55
import ORG.oclc.oai.server.verb.OAIInternalServerError;
56
import ORG.oclc.oai.server.verb.ServerVerb;
57

    
58

    
59
/**
60
 * OAIHandler is the primary Servlet for OAICat.
61
 * 
62
 * @author Jeffrey A. Young, OCLC Online Computer Library Center
63
 */
64
public class OAIHandler extends HttpServlet {
65

    
66
  /*
67
   * Class fields
68
   */
69
  
70
  private static final Logger log = Logger.getLogger(OAIHandler.class);
71
  private static final long serialVersionUID = 1L;
72
  private static final String VERSION = "1.5.57";
73
  private static boolean debug = false;
74
  
75
  // Set to false when developing and testing code outside of Metacat
76
  private static boolean integratedWithMetacat = true;
77

    
78
  
79
  /*
80
   * Class methods
81
   */
82
  
83
  /**
84
   * Get the VERSION number
85
   */
86
  public static String getVERSION() {
87
    return VERSION;
88
  }
89
  
90
  /**
91
   * Boolean to determine whether the Data Provider code is executing in a
92
   * Metacat application. This is normally true, but the 
93
   * 'integratedWithMetacat' value can be set to
94
   * false by a developer when testing the code outside of Metacat. This
95
   * eliminates dependencies on Metacat property settings.
96
   * 
97
   * @return
98
   */
99
  public static boolean isIntegratedWithMetacat() {
100
    return integratedWithMetacat;
101
  }
102

    
103

    
104
  /*
105
   * Instance fields
106
   */
107
  
108
  protected HashMap attributesMap = new HashMap();
109
  private final String CONFIG_DIR = "WEB-INF";
110
  private final String CONFIG_NAME = "metacat.properties";
111
  private final String LOG4J_NAME = "log4j.properties";
112
  private final String XSLT_DIR = "oaipmh";
113

    
114
  
115
  /*
116
   * Instance methods
117
   */
118
  
119
  /**
120
   * Peform the http GET action. Note that POST is shunted to here as well. The
121
   * verb widget is taken from the request and used to invoke an OAIVerb object
122
   * of the corresponding kind to do the actual work of the verb.
123
   * 
124
   * @param request
125
   *          the servlet's request information
126
   * @param response
127
   *          the servlet's response information
128
   * @exception IOException
129
   *              an I/O error occurred
130
   */
131
  public void doGet(HttpServletRequest request, HttpServletResponse response)
132
      throws IOException, ServletException {
133
    ServletConfig servletConfig = this.getServletConfig();
134
    //init_debug(servletConfig); // used when debugging the init() method
135
    String pathInfo = request.getPathInfo();
136
    HashMap attributes = getAttributes(pathInfo);
137
    Properties properties;
138
    
139
    if (!filterRequest(request, response)) { return; }
140
    
141
    log.debug("attributes=" + attributes);
142
    properties = (Properties) attributes.get("OAIHandler.properties");
143
    boolean monitor = false;
144
    
145
    if (properties.getProperty("OAIHandler.monitor") != null) {
146
      monitor = true;
147
    }
148
    
149
    boolean serviceUnavailable = isServiceUnavailable(properties);
150

    
151
    HashMap serverVerbs = ServerVerb.getVerbs(properties);
152
    Transformer transformer = 
153
                         (Transformer) attributes.get("OAIHandler.transformer");
154

    
155
    boolean forceRender = false;
156
    
157
    if ("true".equals(properties.getProperty("OAIHandler.forceRender"))) {
158
      forceRender = true;
159
    }
160

    
161
    // try {
162
    request.setCharacterEncoding("UTF-8");
163
    // } catch (UnsupportedEncodingException e) {
164
    // e.printStackTrace();
165
    // throw new IOException(e.getMessage());
166
    // }
167
    
168
    Date then = null;
169
    if (monitor) then = new Date();
170
    
171
    if (debug) {
172
      Enumeration headerNames = request.getHeaderNames();
173
      System.out.println("OAIHandler.doGet: ");
174
      
175
      while (headerNames.hasMoreElements()) {
176
        String headerName = (String) headerNames.nextElement();
177
        System.out.print(headerName);
178
        System.out.print(": ");
179
        System.out.println(request.getHeader(headerName));
180
      }
181
    }
182
    
183
    if (serviceUnavailable) {
184
      response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
185
          "Sorry. This server is down for maintenance");
186
    } 
187
    else {
188
      try {
189
        String userAgent = request.getHeader("User-Agent");
190
        
191
        if (userAgent == null) {
192
          userAgent = "";
193
        } else {
194
          userAgent = userAgent.toLowerCase();
195
        }
196
        
197
        Transformer serverTransformer = null;
198
        
199
        if (transformer != null) {
200
          // return HTML if the client is an old browser
201
          if (forceRender
202
              || userAgent.indexOf("opera") != -1
203
              || (userAgent.startsWith("mozilla") && userAgent
204
                  .indexOf("msie 6") == -1
205
              /* && userAgent.indexOf("netscape/7") == -1 */)) {
206
            serverTransformer = transformer;
207
          }
208
        }
209
        
210
        String result = getResult(attributes, request, response,
211
                                  serverTransformer, serverVerbs);
212
        
213
        // log.debug("result=" + result);
214
        // if (serverTransformer) { // render on the server
215
        // response.setContentType("text/html; charset=UTF-8");
216
        // StringReader stringReader = new StringReader(getResult(request));
217
        // StreamSource streamSource = new StreamSource(stringReader);
218
        // StringWriter stringWriter = new StringWriter();
219
        // transformer.transform(streamSource, new StreamResult(stringWriter));
220
        // result = stringWriter.toString();
221
        // } else { // render on the client
222
        // response.setContentType("text/xml; charset=UTF-8");
223
        // result = getResult(request);
224
        // }
225

    
226
        Writer out = getWriter(request, response);
227
        out.write(result);
228
        out.close();
229
      } 
230
      catch (FileNotFoundException e) {
231
        if (debug) {
232
          e.printStackTrace();
233
          System.out.println("SC_NOT_FOUND: " + e.getMessage());
234
        }
235
        response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
236
      } 
237
      catch (TransformerException e) {
238
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
239
                           e.getMessage()
240
                          );
241
      } 
242
      catch (OAIInternalServerError e) {
243
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
244
                           e.getMessage()
245
                          );
246
      } 
247
      catch (SocketException e) {
248
        System.out.println(e.getMessage());
249
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
250
                           e.getMessage()
251
                          );
252
      } 
253
      catch (Throwable e) {
254
        e.printStackTrace();
255
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
256
                           e.getMessage()
257
                          );
258
      }
259
    }
260
    
261
    if (monitor) {
262
      StringBuffer reqUri = new StringBuffer(request.getRequestURI().toString());
263
      String queryString = request.getQueryString(); // d=789
264
      
265
      if (queryString != null) {
266
        reqUri.append("?").append(queryString);
267
      }
268
      
269
      Runtime rt = Runtime.getRuntime();
270
      System.out.println(rt.freeMemory() + "/" + rt.totalMemory() + " "
271
          + ((new Date()).getTime() - then.getTime()) + "ms: "
272
          + reqUri.toString());
273
    }
274
  }
275

    
276

    
277
  /**
278
   * Perform a POST action. Actually this gets shunted to GET.
279
   * 
280
   * @param request
281
   *          the servlet's request information
282
   * @param response
283
   *          the servlet's response information
284
   * @exception IOException
285
   *              an I/O error occurred
286
   */
287
  public void doPost(HttpServletRequest request, HttpServletResponse response)
288
      throws IOException, ServletException {
289
    doGet(request, response);
290
  }
291

    
292
  
293
  /**
294
   * Override to do any pre-qualification; return false if the response should be
295
   * returned immediately, without further action.
296
   * 
297
   * @param request
298
   * @param response
299
   * @return false=return immediately, true=continue
300
   */
301
  protected boolean filterRequest(HttpServletRequest request,
302
      HttpServletResponse response) {
303
    return true;
304
  }
305

    
306

    
307
  public HashMap getAttributes(Properties properties) 
308
          throws Throwable {
309
    HashMap attributes = new HashMap();
310
    Enumeration attrNames = getServletContext().getAttributeNames();
311
    String serviceUnavailable;
312
    
313
    while (attrNames.hasMoreElements()) {
314
      String attrName = (String) attrNames.nextElement();
315
      Object attrValue = getServletContext().getAttribute(attrName);
316
      attributes.put(attrName, attrValue);
317
    }
318

    
319
    attributes.put("OAIHandler.properties", properties);
320
    String missingVerbClassName = properties.getProperty(
321
        "OAIHandler.missingVerbClassName", "ORG.oclc.oai.server.verb.BadVerb");
322
    Class missingVerbClass = Class.forName(missingVerbClassName);
323
    attributes.put("OAIHandler.missingVerbClass", missingVerbClass);
324

    
325
    serviceUnavailable = properties.getProperty("OAIHandler.serviceUnavailable");
326
    if (!"true".equals(serviceUnavailable)) {
327
      attributes.put("OAIHandler.version", VERSION);
328
      AbstractCatalog metacatCatalog = 
329
                       AbstractCatalog.factory(properties);
330
      attributes.put("OAIHandler.catalog", metacatCatalog);
331
    }
332
    
333
    boolean forceRender = false;
334
    if ("true".equals(properties.getProperty("OAIHandler.forceRender"))) {
335
      forceRender = true;
336
    }
337

    
338
    String styleSheet = properties.getProperty("OAIHandler.styleSheet");
339
    String appBase = properties.getProperty("OAIHandler.appBase");
340

    
341
    if (appBase == null) appBase = "webapps";
342

    
343
    String render = properties.getProperty("OAIHandler.renderForOldBrowsers");
344
    if ((styleSheet != null) && 
345
        ("true".equalsIgnoreCase(render) || forceRender)
346
       ) {
347
      InputStream is;
348
      
349
      try {
350
        is = new FileInputStream(appBase + "/" + styleSheet);
351
      } 
352
      catch (FileNotFoundException e) {
353
        // This is a silly way to skip the context name in the styleSheet name
354
        is = new FileInputStream(getServletContext().getRealPath(
355
            styleSheet.substring(styleSheet.indexOf("/", 1) + 1)));
356
      }
357
      
358
      StreamSource xslSource = new StreamSource(is);
359
      TransformerFactory tFactory = TransformerFactory.newInstance();
360
      Transformer transformer = tFactory.newTransformer(xslSource);
361
      attributes.put("OAIHandler.transformer", transformer);
362
      is.close();
363
    }
364

    
365
    return attributes;
366
  }
367

    
368

    
369
  public HashMap getAttributes(String pathInfo) {
370
    HashMap attributes = null;
371
    log.debug("pathInfo=" + pathInfo);
372

    
373
    if (pathInfo != null && pathInfo.length() > 0) {
374
      if (attributesMap.containsKey(pathInfo)) {
375
        log.debug("attributesMap containsKey");
376
        attributes = (HashMap) attributesMap.get(pathInfo);
377
      } 
378
      else {
379
        log.debug("!attributesMap containsKey");
380
        
381
        try {
382
          String fileName = pathInfo.substring(1) + ".properties";
383
          log.debug("attempting load of " + fileName);
384
          Thread thread = Thread.currentThread();
385
          ClassLoader classLoader = thread.getContextClassLoader();
386
          InputStream in = classLoader.getResourceAsStream(fileName);
387
          
388
          if (in != null) {
389
            log.debug("file found");
390
            Properties properties = new Properties();
391
            properties.load(in);
392
            attributes = getAttributes(properties);
393
          } 
394
          else {
395
            log.debug("file not found");
396
          }
397
          
398
          attributesMap.put(pathInfo, attributes);
399
        } 
400
        catch (Throwable e) {
401
          log.debug("Couldn't load file", e);
402
          // do nothing
403
        }
404
      }
405
    }
406
    
407
    if (attributes == null) log.debug("use global attributes");
408
    attributes = (HashMap) attributesMap.get("global");
409

    
410
    return attributes;
411
  }
412

    
413

    
414
  public static String getResult(HashMap attributes,
415
                                 HttpServletRequest request, 
416
                                 HttpServletResponse response,
417
                                 Transformer serverTransformer, 
418
                                 HashMap serverVerbs
419
                                ) 
420
      throws Throwable {
421
    String verb = request.getParameter("verb");
422
    log.debug("verb: " + verb);
423
    String result = null;
424

    
425
    if (verb != null) {
426
      if (verb.equals("GetRecord")) {
427
        result = GetRecord.construct(attributes, request, response,
428
          serverTransformer);
429
      } 
430
      else if (verb.equals("Identify")) {
431
        result = Identify.construct(attributes, request, response,
432
          serverTransformer);
433
      } 
434
      else if (verb.equals("ListIdentifiers")) {
435
        result = ListIdentifiers.construct(attributes, request, response,
436
          serverTransformer);
437
      } 
438
      else if (verb.equals("ListMetadataFormats")) {
439
        result = ListMetadataFormats.construct(attributes, request, response,
440
          serverTransformer);
441
      } 
442
      else if (verb.equals("ListRecords")) {
443
        result = ListRecords.construct(attributes, request, response,
444
          serverTransformer);
445
      } 
446
      else if (verb.equals("ListSets")) {
447
        result = ListSets.construct(attributes, request, response,
448
          serverTransformer);
449
      } 
450
      else {
451
        result = BadVerb.construct(attributes, request, response,
452
          serverTransformer);
453
      }
454
    }
455
    else {
456
      // Return a 'badVerb' response when the verb is missing from the request
457
      result = BadVerb.construct(attributes, request, response,
458
          serverTransformer);
459
    }
460

    
461
    return result;
462
    
463
    /*
464
     * } catch (NoSuchMethodException e) { throw new
465
     * OAIInternalServerError(e.getMessage()); } catch (IllegalAccessException
466
     * e) { throw new OAIInternalServerError(e.getMessage()); }
467
     */
468
  }
469

    
470

    
471
  /**
472
   * Get a response Writer depending on acceptable encodings
473
   * 
474
   * @param request
475
   *          the servlet's request information
476
   * @param response
477
   *          the servlet's response information
478
   * @exception IOException
479
   *              an I/O error occurred
480
   */
481
  public static Writer getWriter(HttpServletRequest request,
482
      HttpServletResponse response) throws IOException {
483
    Writer out;
484
    String encodings = request.getHeader("Accept-Encoding");
485
    
486
    if (debug) { System.out.println("encodings=" + encodings); }
487
    
488
    if (encodings != null && encodings.indexOf("gzip") != -1) {
489
      // System.out.println("using gzip encoding");
490
      // log.debug("using gzip encoding");
491
      response.setHeader("Content-Encoding", "gzip");
492
      out = new OutputStreamWriter(new GZIPOutputStream(response
493
          .getOutputStream()), "UTF-8");
494
      // } else if (encodings != null && encodings.indexOf("compress") != -1) {
495
      // // System.out.println("using compress encoding");
496
      // response.setHeader("Content-Encoding", "compress");
497
      // ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
498
      // zos.putNextEntry(new ZipEntry("dummy name"));
499
      // out = new OutputStreamWriter(zos, "UTF-8");
500
    } 
501
    else if (encodings != null && encodings.indexOf("deflate") != -1) {
502
      // System.out.println("using deflate encoding");
503
      // log.debug("using deflate encoding");
504
      response.setHeader("Content-Encoding", "deflate");
505
      out = new OutputStreamWriter(new DeflaterOutputStream(response
506
          .getOutputStream()), "UTF-8");
507
    } 
508
    else {
509
      // log.debug("using no encoding");
510
      out = response.getWriter();
511
    }
512
    
513
    return out;
514
  }
515

    
516

    
517
  /**
518
   * init is called one time when the Servlet is loaded. This is the place where
519
   * one-time initialization is done. Specifically, we load the properties file
520
   * for this application, and create the AbstractCatalog object for subsequent
521
   * use.
522
   * 
523
   * @param config
524
   *          servlet configuration information
525
   * @exception ServletException
526
   *              there was a problem with initialization
527
   */
528
  //public void init_debug(ServletConfig config) throws ServletException {
529
    public void init(ServletConfig config) throws ServletException {
530
        super.init(config);
531

    
532
        if (isIntegratedWithMetacat()) {
533
            try {
534
                PropertyService.getInstance();
535
            } catch (ServiceException se) {
536
                System.err.println("Error in loading properties: " + se.getMessage());
537
            }
538
        }
539

    
540
        ServletContext servletContext = config.getServletContext();
541
        String configDirPath = servletContext.getRealPath(CONFIG_DIR);
542
        String configPath = configDirPath + "/" + CONFIG_NAME;
543

    
544
        // Initialize the properties file for log4j
545
        String log4jPath = configDirPath + "/" + LOG4J_NAME;
546
        PropertyConfigurator.configureAndWatch(log4jPath);
547

    
548
        // Initialize the directory path to the crosswalk XSLT files
549
        String xsltDirPath = servletContext.getRealPath(XSLT_DIR);
550
        Eml2oai_dc.setDirPath(xsltDirPath);
551
        InputStream in = null;
552

    
553
        try {
554
            HashMap attributes = null;
555
            Properties properties = null;
556
            
557
            try {
558
                log.debug("configPath=" + configPath);
559
                in = new FileInputStream(configPath);
560
            } catch (FileNotFoundException e) {
561
                log.debug("configPath not found. Try the classpath: " + configPath);
562
                Thread thread = Thread.currentThread();
563
                ClassLoader classLoader = thread.getContextClassLoader();
564
                in = classLoader.getResourceAsStream(configPath);
565
            }
566

    
567
            if (in != null) {
568
                log.debug("configPath '" + configPath + "' found. Loading properties");
569
                properties = new Properties();
570
                properties.load(in);
571
                attributes = getAttributes(properties);
572
            }
573

    
574
            attributesMap.put("global", attributes);
575
            in.close();
576
        } catch (FileNotFoundException e) {
577
            e.printStackTrace();
578
            throw new ServletException(e.getMessage());
579
        } catch (ClassNotFoundException e) {
580
            e.printStackTrace();
581
            throw new ServletException(e.getMessage());
582
        } catch (IllegalArgumentException e) {
583
            e.printStackTrace();
584
            throw new ServletException(e.getMessage());
585
        } catch (IOException e) {
586
            e.printStackTrace();
587
            throw new ServletException(e.getMessage());
588
        } catch (Throwable e) {
589
            e.printStackTrace();
590
            throw new ServletException(e.getMessage());
591
        } finally {
592
            IOUtils.closeQuietly(in);
593
        }
594
    }
595

    
596

    
597
  /**
598
   * Should the server report itself down for maintenance? Override this method
599
   * if you want to do this check another way.
600
   * 
601
   * @param properties
602
   * @return true=service is unavailable, false=service is available
603
   */
604
  protected boolean isServiceUnavailable(Properties properties) {
605
    if (properties.getProperty("OAIHandler.serviceUnavailable") != null) {
606
      return true;
607
    }
608
    
609
    return false;
610
  }
611

    
612
}
    (1-1/1)