001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.file.remote;
018    
019    import java.io.IOException;
020    
021    import org.apache.camel.Exchange;
022    import org.apache.camel.ServicePoolAware;
023    import org.apache.camel.component.file.GenericFileOperationFailedException;
024    import org.apache.camel.component.file.GenericFileProducer;
025    import org.apache.camel.util.ExchangeHelper;
026    
027    /**
028     * Remote file producer. Handles connecting and disconnecting if we are not.
029     * Generic type F is the remote system implementation of a file.
030     */
031    public class RemoteFileProducer<T> extends GenericFileProducer<T> implements ServicePoolAware {
032    
033        private boolean loggedIn;
034        
035        protected RemoteFileProducer(RemoteFileEndpoint<T> endpoint, RemoteFileOperations<T> operations) {
036            super(endpoint, operations);
037        }
038        
039        @Override
040        protected String getFileSeparator() {
041            return "/";
042        }
043        
044        @Override
045        protected String normalizePath(String name) {        
046            return name;
047        }
048    
049        @SuppressWarnings("unchecked")
050        public void process(Exchange exchange) throws Exception {
051            Exchange remoteExchange = getEndpoint().createExchange(exchange);
052            processExchange(remoteExchange);
053            ExchangeHelper.copyResults(exchange, remoteExchange);
054        }
055    
056        protected RemoteFileOperations getOperations() {
057            return (RemoteFileOperations) operations;
058        }
059    
060        /**
061         * The file could not be written. We need to disconnect from the remote server.
062         */
063        protected void handleFailedWrite(Exchange exchange, Exception exception) throws Exception {
064            loggedIn = false;
065            if (isStopping() || isStopped()) {
066                // if we are stopping then ignore any exception during a poll
067                log.debug("Exception occurred during stopping: " + exception.getMessage());
068            } else {
069                log.warn("Writing file failed with: " + exception.getMessage());
070                try {
071                    disconnect();
072                } catch (Exception e) {
073                    // ignore exception
074                    log.debug("Ignored exception during disconnect: " + e.getMessage());
075                }
076                // rethrow the original exception*/
077                throw exception;
078            }
079        }
080    
081        public void disconnect() throws IOException {
082            loggedIn = false;
083            if (getOperations().isConnected()) {
084                if (log.isDebugEnabled()) {
085                    log.debug("Disconnecting from: " + getEndpoint());
086                }
087                getOperations().disconnect();
088            }
089        }
090    
091        @Override
092        protected void preWriteCheck() throws Exception {
093            // before writing send a noop to see if the connection is alive and works
094            boolean noop = false;
095            try {
096                connectIfNecessary();
097                if (loggedIn) {
098                    noop = getOperations().sendNoop();
099                }
100            } catch (Exception e) {
101                // ignore as we will try to recover connection
102                noop = false;
103            }
104            if (log.isDebugEnabled()) {
105                log.debug("preWriteCheck send noop success: " + noop);
106            }
107    
108            // if not alive then force a disconnect so we reconnect again
109            if (!noop) {
110                try {
111                    if (log.isDebugEnabled()) {
112                        log.debug("preWriteCheck forcing a disconnect as noop failed");
113                    }
114                    disconnect();
115                } catch (Exception e) {
116                    // ignore for now as we will reconnect below
117                }
118            }
119    
120            connectIfNecessary();
121            if (!loggedIn) {
122                // must be logged in to be able to upload the file
123                String message = "Cannot connect/login to: " + ((RemoteFileEndpoint) getEndpoint()).remoteServerInformation();
124                throw new GenericFileOperationFailedException(message);
125            }
126        }
127    
128        @Override
129        protected void doStart() throws Exception {
130            log.debug("Starting");
131            // do not connect when component starts, just wait until we process as we will
132            // connect at that time if needed
133            super.doStart();
134        }
135    
136        @Override
137        protected void doStop() throws Exception {
138            try {
139                disconnect();
140            } catch (Exception e) {
141                log.debug("Exception occurred during disconnecting from: " + getEndpoint() + " " + e.getMessage());
142            }
143            super.doStop();
144        }
145    
146        protected void connectIfNecessary() throws IOException {
147            if (!loggedIn) {
148                if (log.isDebugEnabled()) {
149                    log.debug("Not already connected/logged in. Connecting to: " + getEndpoint());
150                }
151                RemoteFileEndpoint rfe = (RemoteFileEndpoint) getEndpoint();
152                RemoteFileConfiguration conf = (RemoteFileConfiguration) rfe.getConfiguration();
153                loggedIn = getOperations().connect(conf);
154                if (!loggedIn) {
155                    return;
156                }
157                log.info("Connected and logged in to: " + getEndpoint());
158            }
159        }
160    
161        public boolean isSingleton() {
162            // this producer is stateful because the remote file operations is not thread safe
163            return false;
164        }
165    }