예제 #1
0
  @RequestMapping(method = RequestMethod.GET)
  protected String processGet(
      @RequestAttribute Context context,
      ModelMap model,
      HttpServletRequest request,
      HttpServletResponse response)
      throws ServletException, IOException, SQLException, AuthorizeException {

    Group group = null;
    group = checkGroup(context, request);

    if (group != null) {

      // unknown action, show edit page
      model.addAttribute("group", group);
      model.addAttribute("members", group.getMembers());
      model.addAttribute("membergroups", group.getMemberGroups());

      String utilsGrpName = Utils.addEntities(group.getName());
      model.addAttribute("utilsGrpName", utilsGrpName);
      return "pages/admin/group-edit";

    } else {

      return showMainPage(context, model, request, response);
    }
  } // end processGet
예제 #2
0
  /**
   * Factory method for ResultsPruners (used to load ConfigurationManager properties.
   *
   * @param props
   * @throws FileNotFoundException
   */
  public static ResultsPruner getPruner(Context context, Properties props)
      throws FileNotFoundException {

    ResultsPruner rp = new ResultsPruner(context);
    Pattern retentionPattern = Pattern.compile("checker\\.retention\\.(.*)");
    for (Enumeration<String> en = (Enumeration<String>) props.propertyNames();
        en.hasMoreElements(); ) {
      String name = en.nextElement();
      Matcher matcher = retentionPattern.matcher(name);
      if (!matcher.matches()) {
        continue;
      }
      String resultCode = matcher.group(1);
      long duration;
      try {
        duration = Utils.parseDuration(props.getProperty(name));
      } catch (ParseException e) {
        throw new IllegalStateException("Problem parsing duration: " + e.getMessage(), e);
      }
      ChecksumResultCode code = ChecksumResultCode.valueOf(resultCode);
      if (code == null) {
        throw new IllegalStateException("Checksum result code not found: " + resultCode);
      }
      if ("default".equals(resultCode)) {
        rp.setDefaultDuration(duration);
      } else {
        rp.addInterested(code, duration);
      }
    }
    return rp;
  }
예제 #3
0
 /**
  * Get the license for this Item as String. License is the first bitstream named "license.txt" in
  * a LICENSE bundle, apparently?
  *
  * <p>FIXME: is this correct? there's no counterexample..
  *
  * @return license string, or null if none found.
  * @throws SQLException the SQL exception
  * @throws AuthorizeException the authorize exception
  * @throws IOException Signals that an I/O exception has occurred.
  */
 private String getLicenseAsString() throws SQLException, AuthorizeException, IOException {
   Bundle lb[] = this.item.getBundles(Constants.LICENSE_BUNDLE_NAME);
   for (Bundle element : lb) {
     Bitstream lbs = element.getBitstreamByName("license.txt");
     if (lbs != null) {
       ByteArrayOutputStream baos = new ByteArrayOutputStream((int) lbs.getSize());
       Utils.copy(lbs.retrieve(), baos);
       return baos.toString();
     }
   }
   return null;
 }
  /**
   * Add the current date to the item metadata. This looks up the field in which to store this
   * metadata in the configuration sword.updated.field
   *
   * @param item
   * @throws DSpaceSwordException
   */
  protected void setUpdatedDate(Item item, VerboseDescription verboseDescription)
      throws DSpaceSwordException {
    String field = ConfigurationManager.getProperty("swordv2-server", "updated.field");
    if (field == null || "".equals(field)) {
      throw new DSpaceSwordException(
          "No configuration, or configuration is invalid for: sword.updated.field");
    }

    MDValue dc = this.configToDC(field, null);
    item.clearMetadata(dc.getSchema(), dc.getElement(), dc.getQualifier(), MDValue.ANY);
    item.addMetadata(
        dc.getSchema(), dc.getElement(), dc.getQualifier(), null, Utils.asISO8601(new Date()));

    verboseDescription.append("Updated date added to response from item metadata where available");
  }
예제 #5
0
  /**
   * VERY crude dissemination: just look for the first bitstream with the PDF package type, and toss
   * it out. Works on packages importer with this packager, and maybe some others.
   */
  public void disseminate(
      Context context, DSpaceObject dso, PackageParameters params, OutputStream out)
      throws PackageValidationException, CrosswalkException, AuthorizeException, SQLException,
          IOException {
    if (dso.getType() != Constants.ITEM)
      throw new PackageValidationException(
          "This disseminator can only handle objects of type ITEM.");

    Item item = (Item) dso;
    try {
      BitstreamFormat pdff = BitstreamFormat.findByShortDescription(context, BITSTREAM_FORMAT_NAME);
      if (pdff == null)
        throw new PackageValidationException(
            "Cannot find BitstreamFormat \"" + BITSTREAM_FORMAT_NAME + "\"");
      Bitstream pkgBs =
          PackageUtils.getBitstreamByFormat(item, pdff, Constants.DEFAULT_BUNDLE_NAME);
      if (pkgBs == null)
        throw new PackageValidationException(
            "Cannot find Bitstream with format \"" + BITSTREAM_FORMAT_NAME + "\"");
      Utils.copy(pkgBs.retrieve(), out);
    } finally {
    }
  }
  public void processDSpaceObject(
      Context context,
      HttpServletRequest request,
      HttpServletResponse response,
      DSpaceObject dso,
      String extraPathInfo)
      throws ServletException, IOException, SQLException, AuthorizeException {
    // OK, we have a valid URI. What is it?
    if (dso.getType() == Constants.BITSTREAM) {
      // FIXME: Check for if-modified-since header
      Bitstream bitstream = (Bitstream) dso;

      log.info(
          LogManager.getHeader(context, "view_bitstream", "bitstream_id=" + bitstream.getID()));

      // Pipe the bits
      //            InputStream is = bitstream.retrieve();
      InputStream is = BitstreamStorageManager.retrieve(context, bitstream);

      // Set the response MIME type
      response.setContentType(bitstream.getFormat().getMIMEType());

      response.setHeader("Content-Length", String.valueOf(bitstream.getSize()));
      response.setHeader("Content-disposition", "attachment; filename=" + bitstream.getName());

      Utils.bufferedCopy(is, response.getOutputStream());
      is.close();
      response.getOutputStream().flush();
    } else if (dso.getType() == Constants.ITEM) {
      Item item = (Item) dso;

      response.setDateHeader("Last-Modified", item.getLastModified().getTime());

      // Check for if-modified-since header
      long modSince = request.getDateHeader("If-Modified-Since");

      if (modSince != -1 && item.getLastModified().getTime() < modSince) {
        // Item has not been modified since requested date,
        // hence bitstream has not; return 304
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      } else {
        // Display the item page
        displayItem(context, request, response, item);
      }
    } else if (dso.getType() == Constants.COLLECTION) {
      Collection c = (Collection) dso;

      // Store collection location in request
      request.setAttribute("dspace.collection", c);

      /*
       * Find the "parent" community the collection, mainly for
       * "breadcrumbs" FIXME: At the moment, just grab the first community
       * the collection is in. This should probably be more context
       * sensitive when we have multiple inclusion.
       */
      Community[] parents = (Community[]) c.getCommunities().toArray();
      request.setAttribute("dspace.community", parents[0]);

      /*
       * Find all the "parent" communities for the collection for
       * "breadcrumbs"
       */
      request.setAttribute("dspace.communities", getParents(parents[0], true));

      // home page, or forward to another page?
      if ((extraPathInfo == null) || (extraPathInfo.equals("/"))) {
        collectionHome(context, request, response, parents[0], c);
      } else {
        // Forward to another servlet
        request.getRequestDispatcher(extraPathInfo).forward(request, response);
      }
    } else if (dso.getType() == Constants.COMMUNITY) {
      Community c = (Community) dso;

      // Store collection location in request
      request.setAttribute("dspace.community", c);

      /*
       * Find all the "parent" communities for the community
       */
      request.setAttribute("dspace.communities", getParents(c, false));

      // home page, or forward to another page?
      if ((extraPathInfo == null) || (extraPathInfo.equals("/"))) {
        communityHome(context, request, response, c);
      } else {
        // Forward to another servlet
        request.getRequestDispatcher(extraPathInfo).forward(request, response);
      }
    } else {
      // Shouldn't happen. Log and treat as invalid ID
      log.info(
          LogManager.getHeader(
              context,
              "URI not an item, collection or community",
              "identifier=" + dso.getIdentifier().toString()));
      JSPManager.showInvalidIDError(request, response, request.getPathInfo(), -1);

      return;
    }
  }
예제 #7
0
  protected void doDSGet(Context context, HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException, SQLException, AuthorizeException {
    ExternalIdentifierDAO identifierDAO = ExternalIdentifierDAOFactory.getInstance(context);

    Item item = null;
    Bitstream bitstream = null;

    String idString = request.getPathInfo();
    String filenameNoPath = null;
    String fullpath = null;
    String uri = null;

    // Parse URL
    if (idString != null) {
      // Remove leading slash
      if (idString.startsWith("/")) {
        idString = idString.substring(1);
      }

      // Get uri and full file path
      int slashIndex = idString.indexOf('/');
      if (slashIndex != -1) {
        slashIndex = idString.indexOf('/', slashIndex + 1);
        if (slashIndex != -1) {
          uri = idString.substring(0, slashIndex);
          fullpath =
              URLDecoder.decode(idString.substring(slashIndex + 1), Constants.DEFAULT_ENCODING);

          // Get filename with no path
          slashIndex = fullpath.indexOf('/');
          if (slashIndex != -1) {
            String[] pathComponents = fullpath.split("/");
            if (pathComponents.length <= maxDepthGuess + 1) {
              filenameNoPath = pathComponents[pathComponents.length - 1];
            }
          }
        }
      }
    }

    if (uri != null && fullpath != null) {
      // Find the item
      try {
        /*
         * If the original item doesn't have a persistent identifier
         * yet (because it's in the workflow) what we actually have is
         * a URL of the form: db-id/1234 where 1234 is the database ID
         * of the item.
         *
         * FIXME: This first part could be totally omitted now that we
         * have the dsi:x/y format of identification.
         */
        if (uri.startsWith("db-id")) {
          String dbIDString = uri.substring(uri.indexOf('/') + 1);
          int dbID = Integer.parseInt(dbIDString);
          item = ItemDAOFactory.getInstance(context).retrieve(dbID);
        } else {
          ExternalIdentifier identifier = identifierDAO.retrieve(uri);
          ObjectIdentifier oi = identifier.getObjectIdentifier();
          item = (Item) oi.getObject(context);
        }
      } catch (NumberFormatException nfe) {
        // Invalid ID - this will be dealt with below
      }
    }

    if (item != null) {
      // Try to find bitstream with exactly matching name + path
      bitstream = getItemBitstreamByName(item, fullpath);

      if (bitstream == null && filenameNoPath != null) {
        // No match with the full path, but we can try again with
        // only the filename
        bitstream = getItemBitstreamByName(item, filenameNoPath);
      }
    }

    // Did we get a bitstream?
    if (bitstream != null) {
      log.info(
          LogManager.getHeader(
              context, "view_html", "uri=" + uri + ",bitstream_id=" + bitstream.getID()));

      // Set the response MIME type
      response.setContentType(bitstream.getFormat().getMIMEType());

      // Response length
      response.setHeader("Content-Length", String.valueOf(bitstream.getSize()));

      // Pipe the bits
      InputStream is = bitstream.retrieve();

      Utils.bufferedCopy(is, response.getOutputStream());
      is.close();
      response.getOutputStream().flush();
    } else {
      // No bitstream - we got an invalid ID
      log.info(LogManager.getHeader(context, "view_html", "invalid_bitstream_id=" + idString));

      JSPManager.showInvalidIDError(request, response, idString, Constants.BITSTREAM);
    }
  }
예제 #8
0
  @RequestMapping(method = RequestMethod.POST)
  protected String processPost(
      @RequestAttribute Context context,
      ModelMap model,
      HttpServletRequest request,
      HttpServletResponse response)
      throws ServletException, IOException, SQLException, AuthorizeException {

    Group group = null;
    group = checkGroup(context, request);

    if (group != null) {

      // is this user authorized to edit this group?
      AuthorizeManager.authorizeAction(context, group, Constants.ADD);

      boolean submit_edit = (request.getParameter("submit_edit") != null);
      boolean submit_group_update = (request.getParameter("submit_group_update") != null);
      boolean submit_group_delete = (request.getParameter("submit_group_delete") != null);
      boolean submit_confirm_delete = (request.getParameter("submit_confirm_delete") != null);
      boolean submit_cancel_delete = (request.getParameter("submit_cancel_delete") != null);

      // just chosen a group to edit - get group and pass it to
      // group-edit.jsp
      if (submit_edit && !submit_group_update && !submit_group_delete) {
        model.addAttribute("group", group);
        model.addAttribute("members", group.getMembers());
        model.addAttribute("membergroups", group.getMemberGroups());
        String utilsGrpName = Utils.addEntities(group.getName());
        model.addAttribute("utilsGrpName", utilsGrpName);

        return "pages/admin/group-edit";
      } // update the members of the group
      else if (submit_group_update) {
        // first off, did we change the group name?
        String newName = request.getParameter("group_name");

        if (!newName.equals(group.getName())) {
          group.setName(newName);
          group.update();
        }

        int[] eperson_ids = Util.getIntParameters(request, "eperson_id");
        int[] group_ids = Util.getIntParameters(request, "group_ids");

        // now get members, and add new ones and remove missing ones
        EPerson[] members = group.getMembers();
        Group[] membergroups = group.getMemberGroups();

        if (eperson_ids != null) {
          // some epeople were listed, now make group's epeople match
          // given epeople
          Set memberSet = new HashSet();
          Set epersonIDSet = new HashSet();

          // add all members to a set
          for (int x = 0; x < members.length; x++) {
            Integer epersonID = Integer.valueOf(members[x].getID());
            memberSet.add(epersonID);
          }

          // now all eperson_ids are put in a set
          for (int x = 0; x < eperson_ids.length; x++) {
            epersonIDSet.add(Integer.valueOf(eperson_ids[x]));
          }

          // process eperson_ids, adding those to group not already
          // members
          Iterator i = epersonIDSet.iterator();

          while (i.hasNext()) {
            Integer currentID = (Integer) i.next();

            if (!memberSet.contains(currentID)) {
              group.addMember(EPerson.find(context, currentID.intValue()));
            }
          }

          // process members, removing any that aren't in eperson_ids
          for (int x = 0; x < members.length; x++) {
            EPerson e = members[x];

            if (!epersonIDSet.contains(Integer.valueOf(e.getID()))) {
              group.removeMember(e);
            }
          }
        } else {
          // no members found (ids == null), remove them all!

          for (int y = 0; y < members.length; y++) {
            group.removeMember(members[y]);
          }
        }

        if (group_ids != null) {
          // some groups were listed, now make group's member groups
          // match given group IDs
          Set memberSet = new HashSet();
          Set groupIDSet = new HashSet();

          // add all members to a set
          for (int x = 0; x < membergroups.length; x++) {
            Integer myID = Integer.valueOf(membergroups[x].getID());
            memberSet.add(myID);
          }

          // now all eperson_ids are put in a set
          for (int x = 0; x < group_ids.length; x++) {
            groupIDSet.add(Integer.valueOf(group_ids[x]));
          }

          // process group_ids, adding those to group not already
          // members
          Iterator i = groupIDSet.iterator();

          while (i.hasNext()) {
            Integer currentID = (Integer) i.next();

            if (!memberSet.contains(currentID)) {
              group.addMember(Group.find(context, currentID.intValue()));
            }
          }

          // process members, removing any that aren't in eperson_ids
          for (int x = 0; x < membergroups.length; x++) {
            Group g = membergroups[x];

            if (!groupIDSet.contains(Integer.valueOf(g.getID()))) {
              group.removeMember(g);
            }
          }

        } else {
          // no members found (ids == null), remove them all!
          for (int y = 0; y < membergroups.length; y++) {
            group.removeMember(membergroups[y]);
          }
        }

        group.update();

        model.addAttribute("group", group);
        model.addAttribute("members", group.getMembers());
        model.addAttribute("membergroups", group.getMemberGroups());
        String utilsGrpName = Utils.addEntities(group.getName());
        model.addAttribute("utilsGrpName", utilsGrpName);

        context.commit();
        return "pages/admin/group-edit";
      } else if (submit_group_delete) {
        // direct to a confirmation step
        model.addAttribute("group", group);
        return "pages/admin/group-confirm-delete";
      } else if (submit_confirm_delete) {
        // phony authorize, only admins can do this
        AuthorizeManager.authorizeAction(context, group, Constants.WRITE);

        // delete group, return to group-list.jsp
        group.delete();

        return showMainPage(context, model, request, response);
      } else if (submit_cancel_delete) {
        // show group list
        return showMainPage(context, model, request, response);
      } else {
        // unknown action, show edit page
        model.addAttribute("group", group);
        model.addAttribute("members", group.getMembers());
        model.addAttribute("membergroups", group.getMemberGroups());
        String utilsGrpName = Utils.addEntities(group.getName());
        model.addAttribute("utilsGrpName", utilsGrpName);

        return "pages/admin/group-edit";
      }
    } else {

      // want to add a group - create a blank one, and pass to
      // group_edit.jsp
      String button = UIUtil.getSubmitButton(request, "submit");

      if (button.equals("submit_add")) {
        group = Group.create(context);

        group.setName("new group" + group.getID());
        group.update();

        model.addAttribute("group", group);
        model.addAttribute("members", group.getMembers());
        model.addAttribute("membergroups", group.getMemberGroups());
        String utilsGrpName = Utils.addEntities(group.getName());
        model.addAttribute("utilsGrpName", utilsGrpName);

        context.commit();
        return "pages/admin/group-edit";

      } else {
        // show the main page (select groups)
        return showMainPage(context, model, request, response);
      }
    } // end
  } // end processGet
  /**
   * Export the object (Item, Collection, or Community) to a package file on the indicated
   * OutputStream. Gets an exception of the object cannot be packaged or there is a failure creating
   * the package.
   *
   * @param context - DSpace context.
   * @param dso - DSpace object (item, collection, etc)
   * @param pkg - output stream on which to write package
   * @throws PackageException if package cannot be created or there is a fatal error in creating it.
   */
  public void disseminate(
      Context context, DSpaceObject dso, PackageParameters params, OutputStream pkg)
      throws PackageValidationException, CrosswalkException, AuthorizeException, SQLException,
          IOException {
    if (dso.getType() == Constants.ITEM) {
      Item item = (Item) dso;
      long lmTime = item.getLastModified().getTime();

      // how to handle unauthorized bundle/bitstream:
      String unauth = (params == null) ? null : params.getProperty("unauthorized");

      if (params != null && params.getProperty("manifestOnly") != null) {
        extraFiles = null;
        writeManifest(context, item, params, pkg);
      } else {
        extraFiles = new HashMap();
        ZipOutputStream zip = new ZipOutputStream(pkg);
        zip.setComment("METS archive created by DSpace METSDisseminationCrosswalk");

        // write manifest first.
        ZipEntry me = new ZipEntry(MANIFEST_FILE);
        me.setTime(lmTime);
        zip.putNextEntry(me);
        writeManifest(context, item, params, zip);
        zip.closeEntry();

        // copy extra (meta?) bitstreams into zip
        Iterator fi = extraFiles.keySet().iterator();
        while (fi.hasNext()) {
          String fname = (String) fi.next();
          ZipEntry ze = new ZipEntry(fname);
          ze.setTime(lmTime);
          zip.putNextEntry(ze);
          Utils.copy((InputStream) extraFiles.get(fname), zip);
          zip.closeEntry();
        }

        // copy all non-meta bitstreams into zip
        Bundle bundles[] = item.getBundles();
        for (int i = 0; i < bundles.length; i++) {
          if (!PackageUtils.isMetaInfoBundle(bundles[i])) {
            // unauthorized bundle?
            if (!AuthorizeManager.authorizeActionBoolean(context, bundles[i], Constants.READ)) {
              if (unauth != null && (unauth.equalsIgnoreCase("skip"))) {
                log.warn(
                    "Skipping Bundle[\""
                        + bundles[i].getName()
                        + "\"] because you are not authorized to read it.");
                continue;
              } else
                throw new AuthorizeException(
                    "Not authorized to read Bundle named \"" + bundles[i].getName() + "\"");
            }
            Bitstream[] bitstreams = bundles[i].getBitstreams();
            for (int k = 0; k < bitstreams.length; k++) {
              boolean auth =
                  AuthorizeManager.authorizeActionBoolean(context, bitstreams[k], Constants.READ);
              if (auth || (unauth != null && unauth.equalsIgnoreCase("zero"))) {
                ZipEntry ze = new ZipEntry(makeBitstreamName(bitstreams[k]));
                ze.setTime(lmTime);
                ze.setSize(auth ? bitstreams[k].getSize() : 0);
                zip.putNextEntry(ze);
                if (auth) Utils.copy(bitstreams[k].retrieve(), zip);
                else
                  log.warn(
                      "Adding zero-length file for Bitstream, SID="
                          + String.valueOf(bitstreams[k].getSequenceID())
                          + ", not authorized for READ.");
                zip.closeEntry();
              } else if (unauth != null && unauth.equalsIgnoreCase("skip")) {
                log.warn(
                    "Skipping Bitstream, SID="
                        + String.valueOf(bitstreams[k].getSequenceID())
                        + ", not authorized for READ.");
              } else {
                throw new AuthorizeException(
                    "Not authorized to read Bitstream, SID="
                        + String.valueOf(bitstreams[k].getSequenceID()));
              }
            }
          }
        }
        zip.close();
        extraFiles = null;
      }

    } else throw new PackageValidationException("Can only disseminate an Item now.");
  }
예제 #10
0
  public boolean checkPassword(String attempt) {
    String encoded = Utils.getMD5(attempt);

    return (encoded.equals(metadata.get(EPersonMetadataField.PASSWORD)));
  }
예제 #11
0
 public void setPassword(String password) {
   metadata.put(EPersonMetadataField.PASSWORD, Utils.getMD5(password));
   modified = true;
 }
예제 #12
0
 /**
  * Add a result and the length of time before it is removed from the checksum history table.
  *
  * @param result code in the database.
  * @param duration before bitstreams with the specified result type in the checksum history is
  *     removed.
  * @throws ParseException if the duration cannot be parsed into a long value.
  */
 public void addInterested(ChecksumResultCode result, String duration) throws ParseException {
   addInterested(result, Utils.parseDuration(duration));
 }
  @Override
  protected void doDSGet(Context context, HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException, SQLException, AuthorizeException {
    Item item = null;
    Bitstream bitstream = null;

    // Get the ID from the URL
    String idString = request.getPathInfo();
    String handle = "";
    String sequenceText = "";
    String filename = null;
    int sequenceID;

    // Parse 'handle' and 'sequence' (bitstream seq. number) out
    // of remaining URL path, which is typically of the format:
    // {handle}/{sequence}/{bitstream-name}
    // But since the bitstream name MAY have any number of "/"s in
    // it, and the handle is guaranteed to have one slash, we
    // scan from the start to pick out handle and sequence:

    // Remove leading slash if any:
    if (idString.startsWith("/")) {
      idString = idString.substring(1);
    }

    // skip first slash within handle
    int slashIndex = idString.indexOf('/');
    if (slashIndex != -1) {
      slashIndex = idString.indexOf('/', slashIndex + 1);
      if (slashIndex != -1) {
        handle = idString.substring(0, slashIndex);
        int slash2 = idString.indexOf('/', slashIndex + 1);
        if (slash2 != -1) {
          sequenceText = idString.substring(slashIndex + 1, slash2);
          filename = idString.substring(slash2 + 1);
        }
      }
    }

    try {
      sequenceID = Integer.parseInt(sequenceText);
    } catch (NumberFormatException nfe) {
      sequenceID = -1;
    }

    // Now try and retrieve the item
    DSpaceObject dso = HandleManager.resolveToObject(context, handle);

    // Make sure we have valid item and sequence number
    if (dso != null && dso.getType() == Constants.ITEM && sequenceID >= 0) {
      item = (Item) dso;

      if (item.isWithdrawn()) {
        log.info(
            LogManager.getHeader(
                context, "view_bitstream", "handle=" + handle + ",withdrawn=true"));
        JSPManager.showJSP(request, response, "/tombstone.jsp");
        return;
      }

      boolean found = false;

      Bundle[] bundles = item.getBundles();

      for (int i = 0; (i < bundles.length) && !found; i++) {
        Bitstream[] bitstreams = bundles[i].getBitstreams();

        for (int k = 0; (k < bitstreams.length) && !found; k++) {
          if (sequenceID == bitstreams[k].getSequenceID()) {
            bitstream = bitstreams[k];
            found = true;
          }
        }
      }
    }

    if (bitstream == null || filename == null || !filename.equals(bitstream.getName())) {
      // No bitstream found or filename was wrong -- ID invalid
      log.info(LogManager.getHeader(context, "invalid_id", "path=" + idString));
      JSPManager.showInvalidIDError(request, response, idString, Constants.BITSTREAM);

      return;
    }

    log.info(LogManager.getHeader(context, "view_bitstream", "bitstream_id=" + bitstream.getID()));

    // Modification date
    // Only use last-modified if this is an anonymous access
    // - caching content that may be generated under authorisation
    //   is a security problem
    if (context.getCurrentUser() == null) {
      // TODO: Currently the date of the item, since we don't have dates
      // for files
      response.setDateHeader("Last-Modified", item.getLastModified().getTime());

      // Check for if-modified-since header
      long modSince = request.getDateHeader("If-Modified-Since");

      if (modSince != -1 && item.getLastModified().getTime() < modSince) {
        // Item has not been modified since requested date,
        // hence bitstream has not; return 304
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        return;
      }
    }

    // Pipe the bits
    InputStream is = bitstream.retrieve();

    // Set the response MIME type
    response.setContentType(bitstream.getFormat().getMIMEType());

    // Response length
    response.setHeader("Content-Length", String.valueOf(bitstream.getSize()));

    if (threshold != -1 && bitstream.getSize() >= threshold) {
      setBitstreamDisposition(bitstream.getName(), request, response);
    }

    Utils.bufferedCopy(is, response.getOutputStream());
    is.close();
    response.getOutputStream().flush();
  }