001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.ametys.cms.transformation; 018 019import java.net.CookieHandler; 020import java.net.CookieManager; 021import java.net.CookiePolicy; 022import java.util.Collection; 023import java.util.Collections; 024 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.logger.AbstractLogEnabled; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030 031import org.ametys.core.util.HttpUrlUtils; 032import org.ametys.core.util.HttpUrlUtils.HttpCheck; 033import org.ametys.core.version.Version; 034import org.ametys.core.version.VersionsHandler; 035import org.ametys.runtime.i18n.I18nizableText; 036 037/** 038 * Consistency checker component. 039 * Checks the state of the links extracted as outgoing references for a content. 040 */ 041public class ConsistencyChecker extends AbstractLogEnabled implements Serviceable, Component 042{ 043 /** Avalon role */ 044 public static final String ROLE = ConsistencyChecker.class.getName(); 045 046 /** Constants for the special external type for outgoing references */ 047 public static final String CONSISTENCY_EXTERNAL_REFERENCE_TYPE = "__external"; 048 049 /** The state of the check */ 050 public enum CHECK 051 { 052 /** If the check was all right */ 053 SUCCESS, 054 /** Server Error. */ 055 SERVER_ERROR, 056 /** Not Found.*/ 057 NOT_FOUND, 058 /** Unauthorized. */ 059 UNAUTHORIZED, 060 /** If the check may be too long */ 061 UNKNOWN; 062 063 /** 064 * Convert a {@link HttpCheck} value to a {@link CHECK} value 065 * @param httpCheck the HTTP check 066 * @return the CHECK value 067 */ 068 public static CHECK from(HttpCheck httpCheck) 069 { 070 switch (httpCheck) 071 { 072 case SUCCESS: 073 return CHECK.SUCCESS; 074 075 case TIMEOUT: 076 case SECURITY_LEVEL_ERROR: 077 case REDIRECT: 078 return CHECK.UNKNOWN; 079 080 case NOT_FOUND: 081 return CHECK.NOT_FOUND; 082 083 case SERVER_ERROR: 084 default: 085 return CHECK.SERVER_ERROR; 086 } 087 } 088 } 089 090 /** The ametys uri resolver */ 091 protected URIResolverExtensionPoint _uriResolverEP; 092 /** The version handler */ 093 protected VersionsHandler _versionsHandler; 094 /** The URL utils */ 095 protected HttpUrlUtils _urlUtils; 096 097 @Override 098 public void service(ServiceManager manager) throws ServiceException 099 { 100 _uriResolverEP = (URIResolverExtensionPoint) manager.lookup(URIResolverExtensionPoint.ROLE); 101 _versionsHandler = (VersionsHandler) manager.lookup(VersionsHandler.ROLE); 102 _urlUtils = (HttpUrlUtils) manager.lookup(HttpUrlUtils.ROLE); 103 } 104 105 /** 106 * Check the consistency of a reference 107 * @param referenceType The type of the reference to test (can be 'attachment', 'explorer', 'metadata', '__external', etc...) 108 * @param referenceValue The value of the reference to test 109 * @param contentId The id of the content containing the reference 110 * @param dataPath The path of the data containing the reference 111 * @param shortTest true to make a short test, that means that long tests will return UNKNOWN immediately. If false, you will always have a SUCCESS or a FAILURE. 112 * @return CHECK enum value 113 */ 114 public CHECK checkConsistency(String referenceType, String referenceValue, String contentId, String dataPath, boolean shortTest) 115 { 116 if (getLogger().isDebugEnabled()) 117 { 118 getLogger().debug("Checking consistency for URI of type '" + referenceType + "' with uri '" + referenceValue + "'"); 119 } 120 121 URIResolver resolver = _uriResolverEP.getResolverForType(referenceType); 122 if (resolver == null) 123 { 124 // No resolver for external references 125 if (CONSISTENCY_EXTERNAL_REFERENCE_TYPE.equals(referenceType)) 126 { 127 if (referenceValue.startsWith("http:") || referenceValue.startsWith("https:")) 128 { 129 return _checkHTTPLink(referenceValue, shortTest); 130 } 131 else if (referenceValue.startsWith("mailto:")) 132 { 133 return CHECK.SUCCESS; 134 } 135 else if (referenceValue.startsWith("tel:")) 136 { 137 return CHECK.SUCCESS; 138 } 139 } 140 141 if (getLogger().isDebugEnabled()) 142 { 143 getLogger().debug("Cannot test external link '" + referenceValue + "'"); 144 } 145 146 return CHECK.UNKNOWN; 147 } 148 else 149 { 150 String uri = _getURIFromAttachment(referenceType, referenceValue, contentId, dataPath); 151 return resolver.checkLink(uri, shortTest); 152 } 153 } 154 155 /** 156 * Get the label of a reference. 157 * @param referenceType The type of the reference to test (can be 'attachment', 'explorer', 'metadata', '__external', etc...) 158 * @param referenceValue The value of the reference to test 159 * @param contentId The id of the content containing the reference 160 * @param dataPath The path of the data containing the reference 161 * @return the element label. 162 */ 163 public I18nizableText getLabel(String referenceType, String referenceValue, String contentId, String dataPath) 164 { 165 URIResolver resolver = _uriResolverEP.getResolverForType(referenceType); 166 if (resolver == null) 167 { 168 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_EXTERNAL_LABEL", Collections.singletonList(referenceValue)); 169 } 170 else 171 { 172 String uri = _getURIFromAttachment(referenceType, referenceValue, contentId, dataPath); 173 return resolver.getLabel(uri); 174 } 175 } 176 177 private String _getURIFromAttachment(String referenceType, String referenceValue, String contentId, String dataPath) 178 { 179 return "local".equals(referenceType) ? contentId + "@" + dataPath + ";" + referenceValue : referenceValue; 180 } 181 182 /** 183 * Test an http link 184 * @param linkValue The http url to test 185 * @param shortTest true to make a short test (with short timeout) 186 * @return The state. UNKNOWN if test cannot be done fully 187 */ 188 protected CHECK _checkHTTPLink(String linkValue, boolean shortTest) 189 { 190 try 191 { 192 CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); 193 194 String httpUrl = linkValue.replaceAll(" ", "%20"); 195 String userAgent = "Ametys/" + _getCMSVersion(); 196 int timeout = shortTest ? 1000 : -1; 197 int readTimeout = shortTest ? 2000 : -1; 198 HttpCheck checkURL = HttpUrlUtils.checkHttpUrl(httpUrl, userAgent, null, timeout, readTimeout, true); 199 200 return CHECK.from(checkURL); 201 } 202 finally 203 { 204 ((CookieManager) CookieHandler.getDefault()).getCookieStore().removeAll(); 205 } 206 } 207 208 private String _getCMSVersion() 209 { 210 Collection<Version> versions = _versionsHandler.getVersions(); 211 for (Version version : versions) 212 { 213 if (version.getName().equals("CMS")) 214 { 215 return version.getVersion(); 216 } 217 } 218 return null; 219 } 220}