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 org.apache.camel.Exchange;
020    import org.apache.camel.ServicePoolAware;
021    import org.apache.camel.component.file.GenericFileOperationFailedException;
022    import org.apache.camel.component.file.GenericFileProducer;
023    import org.apache.camel.util.ExchangeHelper;
024    
025    /**
026     * Remote file producer. Handles connecting and disconnecting if we are not.
027     * Generic type F is the remote system implementation of a file.
028     */
029    public class RemoteFileProducer<T> extends GenericFileProducer<T> implements ServicePoolAware {
030    
031        private boolean loggedIn;
032        
033        protected RemoteFileProducer(RemoteFileEndpoint<T> endpoint, RemoteFileOperations<T> operations) {
034            super(endpoint, operations);
035        }
036        
037        @Override
038        protected String getFileSeparator() {
039            return "/";
040        }
041        
042        @Override
043        protected String normalizePath(String name) {        
044            return name;
045        }
046    
047        @SuppressWarnings("unchecked")
048        public void process(Exchange exchange) throws Exception {
049            Exchange remoteExchange = getEndpoint().createExchange(exchange);
050            processExchange(remoteExchange);
051            ExchangeHelper.copyResults(exchange, remoteExchange);
052        }
053    
054        protected RemoteFileOperations getOperations() {
055            return (RemoteFileOperations) operations;
056        }
057    
058        @Override
059        @SuppressWarnings("unchecked")
060        public RemoteFileEndpoint<T> getEndpoint() {
061            return (RemoteFileEndpoint<T>) super.getEndpoint();
062        }
063    
064        /**
065         * The file could not be written. We need to disconnect from the remote server.
066         */
067        protected void handleFailedWrite(Exchange exchange, Exception exception) throws Exception {
068            loggedIn = false;
069            if (isStopping() || isStopped()) {
070                // if we are stopping then ignore any exception during a poll
071                log.debug("Exception occurred during stopping: " + exception.getMessage());
072            } else {
073                log.warn("Writing file failed with: " + exception.getMessage());
074                try {
075                    disconnect();
076                } catch (Exception e) {
077                    // ignore exception
078                    log.debug("Ignored exception during disconnect: " + e.getMessage());
079                }
080                // rethrow the original exception*/
081                throw exception;
082            }
083        }
084    
085        public void disconnect() throws GenericFileOperationFailedException {
086            loggedIn = false;
087            if (getOperations().isConnected()) {
088                if (log.isDebugEnabled()) {
089                    log.debug("Disconnecting from: " + getEndpoint());
090                }
091                getOperations().disconnect();
092            }
093        }
094    
095        @Override
096        protected void preWriteCheck() throws Exception {
097            // before writing send a noop to see if the connection is alive and works
098            boolean noop = false;
099            try {
100                connectIfNecessary();
101                if (loggedIn) {
102                    noop = getOperations().sendNoop();
103                }
104            } catch (Exception e) {
105                // ignore as we will try to recover connection
106                noop = false;
107            }
108            if (log.isDebugEnabled()) {
109                log.debug("preWriteCheck send noop success: " + noop);
110            }
111    
112            // if not alive then force a disconnect so we reconnect again
113            if (!noop) {
114                try {
115                    if (log.isDebugEnabled()) {
116                        log.debug("preWriteCheck forcing a disconnect as noop failed");
117                    }
118                    disconnect();
119                } catch (Exception e) {
120                    // ignore for now as we will reconnect below
121                }
122            }
123    
124            connectIfNecessary();
125            if (!loggedIn) {
126                // must be logged in to be able to upload the file
127                String message = "Cannot connect/login to: " + getEndpoint().remoteServerInformation();
128                throw new GenericFileOperationFailedException(message);
129            }
130        }
131    
132        @Override
133        protected void postWriteCheck() {
134            try {
135                if (getEndpoint().isDisconnect()) {
136                    if (log.isTraceEnabled()) {
137                        log.trace("postWriteCheck disconnect from: " + getEndpoint());
138                    }
139                    disconnect();
140                }
141            } catch (GenericFileOperationFailedException e) {
142                // ignore just log a warning
143                log.warn("Exception occurred during disconnecting from: " + getEndpoint() + " " + e.getMessage());
144            }
145        }
146    
147        @Override
148        protected void doStart() throws Exception {
149            log.debug("Starting");
150            // do not connect when component starts, just wait until we process as we will
151            // connect at that time if needed
152            super.doStart();
153        }
154    
155        @Override
156        protected void doStop() throws Exception {
157            try {
158                disconnect();
159            } catch (Exception e) {
160                log.debug("Exception occurred during disconnecting from: " + getEndpoint() + " " + e.getMessage());
161            }
162            super.doStop();
163        }
164    
165        protected void connectIfNecessary() throws GenericFileOperationFailedException {
166            if (!loggedIn) {
167                if (log.isDebugEnabled()) {
168                    log.debug("Not already connected/logged in. Connecting to: " + getEndpoint());
169                }
170                RemoteFileConfiguration config = (RemoteFileConfiguration) getEndpoint().getConfiguration();
171                loggedIn = getOperations().connect(config);
172                if (!loggedIn) {
173                    return;
174                }
175                log.info("Connected and logged in to: " + getEndpoint());
176            }
177        }
178    
179        public boolean isSingleton() {
180            // this producer is stateful because the remote file operations is not thread safe
181            return false;
182        }
183    
184    }