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;
422

    
423
    if (verb.equals("GetRecord")) {
424
      result = GetRecord.construct(attributes, request, response,
425
          serverTransformer);
426
    } 
427
    else if (verb.equals("Identify")) {
428
      result = Identify.construct(attributes, request, response,
429
          serverTransformer);
430
    } 
431
    else if (verb.equals("ListIdentifiers")) {
432
      result = ListIdentifiers.construct(attributes, request, response,
433
          serverTransformer);
434
    } 
435
    else if (verb.equals("ListMetadataFormats")) {
436
      result = ListMetadataFormats.construct(attributes, request, response,
437
          serverTransformer);
438
    } 
439
    else if (verb.equals("ListRecords")) {
440
      result = ListRecords.construct(attributes, request, response,
441
          serverTransformer);
442
    } 
443
    else if (verb.equals("ListSets")) {
444
      result = ListSets.construct(attributes, request, response,
445
          serverTransformer);
446
    } 
447
    else {
448
      result = BadVerb.construct(attributes, request, response,
449
          serverTransformer);
450
    }
451

    
452
    return result;
453
    
454
    /*
455
     * } catch (NoSuchMethodException e) { throw new
456
     * OAIInternalServerError(e.getMessage()); } catch (IllegalAccessException
457
     * e) { throw new OAIInternalServerError(e.getMessage()); }
458
     */
459
  }
460

    
461

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

    
507

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

    
523
    if (isIntegratedWithMetacat()) {
524
      try {
525
        PropertyService.getInstance();
526
      } 
527
      catch (ServiceException se) {
528
        System.err.println("Error in loading properties: " + se.getMessage());
529
      }
530
    }
531
    
532
    ServletContext servletContext = config.getServletContext();
533
    String configDirPath = servletContext.getRealPath(CONFIG_DIR);
534
    String configPath = configDirPath + "/" + CONFIG_NAME;
535

    
536
    // Initialize the properties file for log4j
537
    String log4jPath = configDirPath + "/" + LOG4J_NAME;
538
    PropertyConfigurator.configureAndWatch(log4jPath);
539
    
540
    // Initialize the directory path to the crosswalk XSLT files
541
    String xsltDirPath = servletContext.getRealPath(XSLT_DIR);
542
    Eml2oai_dc.setDirPath(xsltDirPath);
543

    
544
    try {
545
      HashMap attributes = null;
546
      Properties properties = null;
547
      InputStream in;
548

    
549
      try {
550
        log.debug("configPath=" + configPath);
551
        in = new FileInputStream(configPath);
552
      } 
553
      catch (FileNotFoundException e) {
554
        log.debug("configPath not found. Try the classpath: " + configPath);
555
        Thread thread = Thread.currentThread();
556
        ClassLoader classLoader = thread.getContextClassLoader();
557
        in = classLoader.getResourceAsStream(configPath);
558
      }
559

    
560
      if (in != null) {
561
        log.debug("configPath '" + configPath + "' found. Loading properties");
562
        properties = new Properties();
563
        properties.load(in);
564
        attributes = getAttributes(properties);
565
      }
566

    
567
      attributesMap.put("global", attributes);
568
    }
569
    catch (FileNotFoundException e) {
570
      e.printStackTrace();
571
      throw new ServletException(e.getMessage());
572
    } 
573
    catch (ClassNotFoundException e) {
574
      e.printStackTrace();
575
      throw new ServletException(e.getMessage());
576
    } 
577
    catch (IllegalArgumentException e) {
578
      e.printStackTrace();
579
      throw new ServletException(e.getMessage());
580
    } 
581
    catch (IOException e) {
582
      e.printStackTrace();
583
      throw new ServletException(e.getMessage());
584
    } 
585
    catch (Throwable e) {
586
      e.printStackTrace();
587
      throw new ServletException(e.getMessage());
588
    }
589
  }
590

    
591

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

    
607
}
    (1-1/1)