/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.synapse.transport.vfs;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.axis2.transport.OutTransportInfo;
import org.apache.axis2.transport.base.AbstractTransportSender;
import org.apache.axis2.transport.base.BaseUtils;
import org.apache.axis2.transport.base.ManagementSupport;
import org.apache.commons.io.output.CountingOutputStream;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import java.io.IOException;
/**
* axis2.xml - transport definition
* <transportSender name="file" class="org.apache.synapse.transport.vfs.VFSTransportSender">
* <parameter name="transport.vfs.Locking">enable|disable</parameter> ?
* </transportSender>
*/
public class VFSTransportSender extends AbstractTransportSender implements ManagementSupport {
public static final String TRANSPORT_NAME = "vfs";
/** The VFS file system manager */
private FileSystemManager fsManager = null;
/**
* By default file locking in VFS transport is turned on at a global level
*
* NOTE: DO NOT USE THIS FLAG, USE PollTableEntry#isFileLockingEnabled() TO CHECK WHETHR
* FILE LOCKING IS ENABLED
*/
private boolean globalFileLockingFlag = true;
/**
* The public constructor
*/
public VFSTransportSender() {
log = LogFactory.getLog(VFSTransportSender.class);
}
/**
* Initialize the VFS file system manager and be ready to send messages
* @param cfgCtx the axis2 configuration context
* @param transportOut the transport-out description
* @throws AxisFault on error
*/
public void init(ConfigurationContext cfgCtx, TransportOutDescription transportOut)
throws AxisFault {
super.init(cfgCtx, transportOut);
try {
StandardFileSystemManager fsm = new StandardFileSystemManager();
fsm.setConfiguration(getClass().getClassLoader().getResource("providers.xml"));
fsm.init();
fsManager = fsm;
Parameter lckFlagParam = transportOut.getParameter(VFSConstants.TRANSPORT_FILE_LOCKING);
if (lckFlagParam != null) {
String strLockingFlag = lckFlagParam.getValue().toString();
// by-default enabled, if explicitly specified as "disable" make it disable
if (VFSConstants.TRANSPORT_FILE_LOCKING_DISABLED.equals(strLockingFlag)) {
globalFileLockingFlag = false;
}
}
} catch (FileSystemException e) {
handleException("Error initializing the file transport : " + e.getMessage(), e);
}
}
/**
* Send the given message over the VFS transport
*
* @param msgCtx the axis2 message context
* @throws AxisFault on error
*/
public void sendMessage(MessageContext msgCtx, String targetAddress,
OutTransportInfo outTransportInfo) throws AxisFault {
if (waitForSynchronousResponse(msgCtx)) {
throw new AxisFault("The VFS transport doesn't support synchronous responses. " +
"Please use the appropriate (out only) message exchange pattern.");
}
VFSOutTransportInfo vfsOutInfo = null;
if (targetAddress != null) {
vfsOutInfo = new VFSOutTransportInfo(targetAddress, globalFileLockingFlag);
} else if (outTransportInfo != null && outTransportInfo instanceof VFSOutTransportInfo) {
vfsOutInfo = (VFSOutTransportInfo) outTransportInfo;
}
if (vfsOutInfo != null) {
FileObject replyFile = null;
try {
boolean wasError = true;
int retryCount = 0;
int maxRetryCount = vfsOutInfo.getMaxRetryCount();
long reconnectionTimeout = vfsOutInfo.getReconnectTimeout();
boolean append = vfsOutInfo.isAppend();
while (wasError) {
try {
retryCount++;
replyFile = fsManager.resolveFile(vfsOutInfo.getOutFileURI());
if (replyFile == null) {
log.error("replyFile is null");
throw new FileSystemException("replyFile is null");
}
wasError = false;
} catch (FileSystemException e) {
log.error("cannot resolve replyFile", e);
if(maxRetryCount <= retryCount) {
handleException("cannot resolve replyFile repeatedly: "
+ e.getMessage(), e);
}
}
if (wasError) {
try {
Thread.sleep(reconnectionTimeout);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}
}
if (replyFile.exists()) {
if (replyFile.getType() == FileType.FOLDER) {
// we need to write a file containing the message to this folder
FileObject responseFile = fsManager.resolveFile(replyFile,
VFSUtils.getFileName(msgCtx, vfsOutInfo));
// if file locking is not disabled acquire the lock
// before uploading the file
if (vfsOutInfo.isFileLockingEnabled()) {
acquireLockForSending(responseFile, vfsOutInfo);
if (!responseFile.exists()) {
responseFile.createFile();
}
populateResponseFile(responseFile, msgCtx,append, true);
VFSUtils.releaseLock(fsManager, responseFile);
} else {
if (!responseFile.exists()) {
responseFile.createFile();
}
populateResponseFile(responseFile, msgCtx,append, false);
}
} else if (replyFile.getType() == FileType.FILE) {
// if file locking is not disabled acquire the lock
// before uploading the file
if (vfsOutInfo.isFileLockingEnabled()) {
acquireLockForSending(replyFile, vfsOutInfo);
populateResponseFile(replyFile, msgCtx, append, true);
VFSUtils.releaseLock(fsManager, replyFile);
} else {
populateResponseFile(replyFile, msgCtx, append, false);
}
} else {
handleException("Unsupported reply file type : " + replyFile.getType() +
" for file : " + vfsOutInfo.getOutFileURI());
}
} else {
// if file locking is not disabled acquire the lock before uploading the file
if (vfsOutInfo.isFileLockingEnabled()) {
acquireLockForSending(replyFile, vfsOutInfo);
replyFile.createFile();
populateResponseFile(replyFile, msgCtx, append, true);
VFSUtils.releaseLock(fsManager, replyFile);
} else {
replyFile.createFile();
populateResponseFile(replyFile, msgCtx, append, false);
}
}
} catch (FileSystemException e) {
handleException("Error resolving reply file : " +
vfsOutInfo.getOutFileURI(), e);
} finally {
if (replyFile != null) {
try {
replyFile.close();
} catch (FileSystemException ignore) {}
}
}
} else {
handleException("Unable to determine out transport information to send message");
}
}
private void populateResponseFile(FileObject responseFile, MessageContext msgContext,
boolean append, boolean lockingEnabled) throws AxisFault {
MessageFormatter messageFormatter = BaseUtils.getMessageFormatter(msgContext);
OMOutputFormat format = BaseUtils.getOMOutputFormat(msgContext);
try {
CountingOutputStream os = new CountingOutputStream(
responseFile.getContent().getOutputStream(append));
try {
messageFormatter.writeTo(msgContext, format, os, true);
} finally {
os.close();
}
// update metrics
metrics.incrementMessagesSent(msgContext);
metrics.incrementBytesSent(msgContext, os.getByteCount());
} catch (FileSystemException e) {
if (lockingEnabled) {
VFSUtils.releaseLock(fsManager, responseFile);
}
metrics.incrementFaultsSending();
handleException("IO Error while creating response file : " + responseFile.getName(), e);
} catch (IOException e) {
if (lockingEnabled) {
VFSUtils.releaseLock(fsManager, responseFile);
}
metrics.incrementFaultsSending();
handleException("IO Error while creating response file : " + responseFile.getName(), e);
}
}
private void acquireLockForSending(FileObject responseFile, VFSOutTransportInfo vfsOutInfo)
throws AxisFault {
int tryNum = 0;
// wait till we get the lock
while (!VFSUtils.acquireLock(fsManager, responseFile)) {
if (vfsOutInfo.getMaxRetryCount() == tryNum++) {
handleException("Couldn't send the message to file : "
+ responseFile.getName() + ", unable to acquire the " +
"lock even after " + tryNum + " retries");
} else {
log.warn("Couldn't get the lock for the file : "
+ responseFile.getName() + ", retry : " + tryNum
+ " scheduled after : " + vfsOutInfo.getReconnectTimeout());
try {
Thread.sleep(vfsOutInfo.getReconnectTimeout());
} catch (InterruptedException ignore) {}
}
}
}
}