001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.http.api; 019 020import static org.fcrepo.kernel.api.RdfLexicon.EXTERNAL_CONTENT; 021import static org.slf4j.LoggerFactory.getLogger; 022 023import java.util.List; 024import java.util.stream.Collectors; 025 026import org.fcrepo.kernel.api.exception.ExternalMessageBodyException; 027import org.slf4j.Logger; 028 029/** 030 * Constructs ExternalContentHandler objects from link headers 031 * 032 * @author bbpennel 033 */ 034public class ExternalContentHandlerFactory { 035 036 private static final Logger LOGGER = getLogger(ExternalContentHandlerFactory.class); 037 038 private ExternalContentPathValidator validator; 039 040 /** 041 * Looks for ExternalContent link header and if it finds one it will return a new ExternalContentHandler object 042 * based on the found Link header. If multiple external content headers were found or the URI provided in the 043 * header is not a valid external content path, then an ExternalMessageBodyException will be thrown. 044 * 045 * @param links links from the request header 046 * @return External Content Handler Object if Link header found, else null 047 * @throws ExternalMessageBodyException thrown if more than one external content link was provided, or if the URL 048 * of the header was not a valid external content path. 049 */ 050 public ExternalContentHandler createFromLinks(final List<String> links) throws ExternalMessageBodyException { 051 if (links == null) { 052 return null; 053 } 054 055 final List<String> externalContentLinks = links.stream() 056 .filter(x -> x.contains(EXTERNAL_CONTENT.toString())) 057 .collect(Collectors.toList()); 058 059 if (externalContentLinks.size() > 1) { 060 // got a problem, you can only have one ExternalContent links 061 throw new ExternalMessageBodyException("More then one External Content Link header in request"); 062 } else if (externalContentLinks.size() == 1) { 063 final String link = externalContentLinks.get(0); 064 final String uri = getUriString(link); 065 // Validate that the URI is valid according to allowed set of external uris 066 try { 067 validator.validate(uri); 068 } catch (final ExternalMessageBodyException e) { 069 LOGGER.warn("Rejected invalid external path {}", uri); 070 throw e; 071 } 072 073 return new ExternalContentHandler(link); 074 } 075 076 return null; 077 } 078 079 private static String getUriString(final String link) { 080 final String value = link.trim(); 081 if (value.startsWith("<")) { 082 final int gtIndex = value.indexOf('>'); 083 if (gtIndex != -1) { 084 return value.substring(1, gtIndex).trim(); 085 } else { 086 throw new IllegalArgumentException("Missing token > in " + value); 087 } 088 } else { 089 throw new IllegalArgumentException("Missing starting token < in " + value); 090 } 091 } 092 093 /** 094 * Set the external content path validator 095 * 096 * @param validator validator 097 */ 098 public void setValidator(final ExternalContentPathValidator validator) { 099 this.validator = validator; 100 } 101}