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.log4j.Logger;
40
import org.apache.log4j.PropertyConfigurator;
41

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

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

    
57

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

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

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

    
102

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

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

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

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

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

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

    
275

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

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

    
305

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

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

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

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

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

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

    
363
    return attributes;
364
  }
365

    
366

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

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

    
408
    return attributes;
409
  }
410

    
411

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

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

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

    
468

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

    
514

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

    
530
    if (isIntegratedWithMetacat()) {
531
      try {
532
        PropertyService.getInstance();
533
      } 
534
      catch (ServiceException se) {
535
        System.err.println("Error in loading properties: " + se.getMessage());
536
      }
537
    }
538
    
539
    ServletContext servletContext = config.getServletContext();
540
    String configDirPath = servletContext.getRealPath(CONFIG_DIR);
541
    String configPath = configDirPath + "/" + CONFIG_NAME;
542

    
543
    // Initialize the properties file for log4j
544
    String log4jPath = configDirPath + "/" + LOG4J_NAME;
545
    PropertyConfigurator.configureAndWatch(log4jPath);
546
    
547
    // Initialize the directory path to the crosswalk XSLT files
548
    String xsltDirPath = servletContext.getRealPath(XSLT_DIR);
549
    Eml2oai_dc.setDirPath(xsltDirPath);
550

    
551
    try {
552
      HashMap attributes = null;
553
      Properties properties = null;
554
      InputStream in;
555

    
556
      try {
557
        log.debug("configPath=" + configPath);
558
        in = new FileInputStream(configPath);
559
      } 
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
    }
576
    catch (FileNotFoundException e) {
577
      e.printStackTrace();
578
      throw new ServletException(e.getMessage());
579
    } 
580
    catch (ClassNotFoundException e) {
581
      e.printStackTrace();
582
      throw new ServletException(e.getMessage());
583
    } 
584
    catch (IllegalArgumentException e) {
585
      e.printStackTrace();
586
      throw new ServletException(e.getMessage());
587
    } 
588
    catch (IOException e) {
589
      e.printStackTrace();
590
      throw new ServletException(e.getMessage());
591
    } 
592
    catch (Throwable e) {
593
      e.printStackTrace();
594
      throw new ServletException(e.getMessage());
595
    }
596
  }
597

    
598

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

    
614
}
    (1-1/1)