mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Merged V2.1 to HEAD
6327: Drops alfresco-deployment.zip in build/dist instead of build. 6328:Set AVM file name requirements as lenient as possible. 6329: Fixed DNS name restriction to allow 1-char host labels. 6330: Setting read-only flag for start up components (AR-1621 and AR-193) 6331: Minor formatting 6332: Implementation of a read-only, HTTP-based ContentStore. 6333: AR-1619: A debug message needed changing as the FileFolderService now supports adding children onto system nodes. 6334: Build fix 6335: Fix for AWC-1447 (Office Add-In - Create Collaboration Space) 6337: Fixed AR-1622: WebService requests must be wrapped in retries git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6721 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -24,24 +24,34 @@
|
||||
*/
|
||||
package org.alfresco.repo.content.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.repo.content.AbstractContentReader;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.TicketComponent;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* The reader that does the actual communication with the Alfresco HTTP
|
||||
@@ -53,29 +63,45 @@ import org.apache.commons.httpclient.methods.GetMethod;
|
||||
*/
|
||||
public class HttpAlfrescoContentReader extends AbstractContentReader
|
||||
{
|
||||
private static final String DEFAULT_URL = "{0}/dr?contentUrl={1}?ticket={2}";
|
||||
private static final String INFO_ONLY = "?infoOnly=true";
|
||||
private static final String ERR_NO_CONNECTION = "content.http_reader.err.no_connection";
|
||||
private static final String ERR_NO_AUTHENTICATION = "content.http_reader.err.no_authentication";
|
||||
private static final String ERR_CHECK_CLUSTER = "content.http_reader.err.check_cluster";
|
||||
private static final String ERR_UNRECOGNIZED = "content.http_reader.err.unrecognized";
|
||||
|
||||
AuthenticationService authenticationService;
|
||||
AuthenticationComponent authenticationComponent;
|
||||
private static final String DEFAULT_URL = "{0}/dr?contentUrl={1}&ticket={2}";
|
||||
private static final String INFO_ONLY = "&infoOnly=true";
|
||||
|
||||
private static Log logger = LogFactory.getLog(HttpAlfrescoContentReader.class);
|
||||
|
||||
private TransactionService transactionService;
|
||||
private AuthenticationService authenticationService;
|
||||
private String baseHttpUrl;
|
||||
private String contentUrl;
|
||||
// Helpers
|
||||
private HttpClient httpClient;
|
||||
private PropagateTicketCallback ticketCallback;
|
||||
// Cached values
|
||||
private boolean isInfoCached;
|
||||
private boolean cachedExists;
|
||||
private long cachedLastModified;
|
||||
private long cachedSize;
|
||||
|
||||
public HttpAlfrescoContentReader(
|
||||
TransactionService transactionService,
|
||||
AuthenticationService authenticationService,
|
||||
AuthenticationComponent authenticationComponent,
|
||||
String baseHttpUrl,
|
||||
String contentUrl)
|
||||
{
|
||||
super(contentUrl);
|
||||
this.transactionService = transactionService;
|
||||
this.authenticationService = authenticationService;
|
||||
this.authenticationComponent = authenticationComponent;
|
||||
this.baseHttpUrl = baseHttpUrl;
|
||||
this.contentUrl = contentUrl;
|
||||
// Helpers
|
||||
this.httpClient = new HttpClient();
|
||||
this.ticketCallback = new PropagateTicketCallback();
|
||||
// A trip to the remote server has not been made
|
||||
cachedExists = false;
|
||||
cachedSize = 0L;
|
||||
cachedLastModified = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,31 +114,99 @@ public class HttpAlfrescoContentReader extends AbstractContentReader
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean exists()
|
||||
/**
|
||||
* Helper class to wrap the ticket creation in order to force propagation of the
|
||||
* authentication ticket around the cluster.
|
||||
*
|
||||
* @since 2.1
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
private class PropagateTicketCallback implements RetryingTransactionCallback<String>
|
||||
{
|
||||
public String execute() throws Throwable
|
||||
{
|
||||
return authenticationService.getCurrentTicket();
|
||||
}
|
||||
}
|
||||
|
||||
private void getInfo()
|
||||
{
|
||||
String contentUrl = getContentUrl();
|
||||
// Info will be cached
|
||||
isInfoCached = true;
|
||||
// Authenticate as the system user for the call
|
||||
Authentication authentication = null;
|
||||
GetMethod method = null;
|
||||
try
|
||||
{
|
||||
authenticationComponent.setSystemUserAsCurrentUser();
|
||||
String ticket = authenticationService.getCurrentTicket();
|
||||
authentication = AuthenticationUtil.setCurrentUser(AuthenticationUtil.SYSTEM_USER_NAME);
|
||||
String ticket = transactionService.getRetryingTransactionHelper().doInTransaction(ticketCallback, false, true);
|
||||
String url = HttpAlfrescoContentReader.generateURL(baseHttpUrl, contentUrl, ticket, true);
|
||||
|
||||
method = new GetMethod(url);
|
||||
int statusCode = httpClient.executeMethod(method);
|
||||
if (statusCode == HttpServletResponse.SC_NO_CONTENT)
|
||||
if (statusCode == HttpServletResponse.SC_OK)
|
||||
{
|
||||
return false;
|
||||
// Get the information values from the request
|
||||
String responseSize = method.getResponseHeader("alfresco.dr.size").getValue();
|
||||
String responseLastModified = method.getResponseHeader("alfresco.dr.lastModified").getValue();
|
||||
String responseMimetype = method.getResponseHeader("alfresco.dr.mimetype").getValue();
|
||||
String responseEncoding = method.getResponseHeader("alfresco.dr.encoding").getValue();
|
||||
String responseLocale = method.getResponseHeader("alfresco.dr.locale").getValue();
|
||||
// Fill in this reader's values
|
||||
cachedSize = DefaultTypeConverter.INSTANCE.convert(Long.class, responseSize);
|
||||
cachedLastModified = DefaultTypeConverter.INSTANCE.convert(Date.class, responseLastModified).getTime();
|
||||
setMimetype(DefaultTypeConverter.INSTANCE.convert(String.class, responseMimetype));
|
||||
setEncoding(DefaultTypeConverter.INSTANCE.convert(String.class, responseEncoding));
|
||||
setLocale(DefaultTypeConverter.INSTANCE.convert(Locale.class, responseLocale));
|
||||
// It exists
|
||||
cachedExists = true;
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("\n" +
|
||||
"HttpReader content found: \n" +
|
||||
" Reader: " + this + "\n" +
|
||||
" Server: " + baseHttpUrl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
// Check the return codes
|
||||
if (statusCode == HttpServletResponse.SC_NOT_FOUND)
|
||||
{
|
||||
// It doesn't exist, which is not an error. The defaults are fine.
|
||||
}
|
||||
else if (statusCode == HttpServletResponse.SC_FORBIDDEN)
|
||||
{
|
||||
// If the authentication fails, then the server is there, but probably not
|
||||
// clustered correctly.
|
||||
logger.error(I18NUtil.getMessage(ERR_NO_AUTHENTICATION, baseHttpUrl));
|
||||
logger.error(I18NUtil.getMessage(ERR_CHECK_CLUSTER));
|
||||
}
|
||||
else
|
||||
{
|
||||
// What is this?
|
||||
logger.error(I18NUtil.getMessage(ERR_UNRECOGNIZED, baseHttpUrl, contentUrl, statusCode));
|
||||
logger.error(I18NUtil.getMessage(ERR_CHECK_CLUSTER));
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("\n" +
|
||||
"HttpReader content not found: \n" +
|
||||
" Reader: " + this + "\n" +
|
||||
" Server: " + baseHttpUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConnectException e)
|
||||
{
|
||||
logger.error(I18NUtil.getMessage(ERR_NO_CONNECTION, baseHttpUrl));
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Reader exists check failed: " + this);
|
||||
throw new AlfrescoRuntimeException("Reader exists check failed: " + this, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -120,30 +214,114 @@ public class HttpAlfrescoContentReader extends AbstractContentReader
|
||||
{
|
||||
try { method.releaseConnection(); } catch (Throwable e) {}
|
||||
}
|
||||
authenticationComponent.setCurrentAuthentication(authentication);
|
||||
AuthenticationUtil.setCurrentAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLastModified()
|
||||
|
||||
public synchronized boolean exists()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
if (!isInfoCached)
|
||||
{
|
||||
getInfo();
|
||||
}
|
||||
return cachedExists;
|
||||
}
|
||||
public synchronized long getLastModified()
|
||||
{
|
||||
if (!isInfoCached)
|
||||
{
|
||||
getInfo();
|
||||
}
|
||||
return cachedLastModified;
|
||||
}
|
||||
|
||||
public long getSize()
|
||||
public synchronized long getSize()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
if (!isInfoCached)
|
||||
{
|
||||
getInfo();
|
||||
}
|
||||
return cachedSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContentReader createReader() throws ContentIOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
return new HttpAlfrescoContentReader(transactionService, authenticationService, baseHttpUrl, getContentUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
String contentUrl = getContentUrl();
|
||||
|
||||
Authentication authentication = null;
|
||||
try
|
||||
{
|
||||
if (!exists())
|
||||
{
|
||||
throw new IOException("Content doesn't exist");
|
||||
}
|
||||
authentication = AuthenticationUtil.setCurrentUser(AuthenticationUtil.SYSTEM_USER_NAME);
|
||||
String ticket = transactionService.getRetryingTransactionHelper().doInTransaction(ticketCallback, false, true);
|
||||
String url = HttpAlfrescoContentReader.generateURL(baseHttpUrl, contentUrl, ticket, false);
|
||||
|
||||
GetMethod method = new GetMethod(url);
|
||||
int statusCode = httpClient.executeMethod(method);
|
||||
if (statusCode == HttpServletResponse.SC_OK)
|
||||
{
|
||||
// Get the stream from the request
|
||||
InputStream contentStream = method.getResponseBodyAsStream();
|
||||
// Attach a listener to the stream to ensure that the HTTP request is cleaned up
|
||||
this.addListener(new StreamCloseListener(method));
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("\n" +
|
||||
"HttpReader retrieve intput stream: \n" +
|
||||
" Reader: " + this + "\n" +
|
||||
" Server: " + baseHttpUrl);
|
||||
}
|
||||
return Channels.newChannel(contentStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The content exists, but we failed to get it
|
||||
throw new IOException("Failed to get content remote content that supposedly exists.");
|
||||
}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new ContentIOException(
|
||||
"Failed to open stream: \n" +
|
||||
" Reader: " + this + "\n" +
|
||||
" Remote server: " + baseHttpUrl,
|
||||
e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
AuthenticationUtil.setCurrentAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener to ensure that the HTTP method gets closed when the content stream it
|
||||
* is serving to the reader is closed.
|
||||
*
|
||||
* @since 2.1
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
private static class StreamCloseListener implements ContentStreamListener
|
||||
{
|
||||
private GetMethod getMethod;
|
||||
private StreamCloseListener(GetMethod getMethod)
|
||||
{
|
||||
this.getMethod = getMethod;
|
||||
}
|
||||
public void contentStreamClosed() throws ContentIOException
|
||||
{
|
||||
try { getMethod.releaseConnection(); } catch (Throwable e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user