Add session to cache with both hostname and address.
[cyberduck.git] / source / ch / cyberduck / core / threading / SessionBackgroundAction.java
blob355e6f38981e64901bdb7256f7cac05f5d988876
1 package ch.cyberduck.core.threading;
3 /*
4 * Copyright (c) 2002-2013 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to:
18 * feedback@cyberduck.ch
21 import ch.cyberduck.core.BookmarkNameProvider;
22 import ch.cyberduck.core.Cache;
23 import ch.cyberduck.core.ConnectionService;
24 import ch.cyberduck.core.HostKeyCallback;
25 import ch.cyberduck.core.LoginCallback;
26 import ch.cyberduck.core.LoginConnectionService;
27 import ch.cyberduck.core.LoginService;
28 import ch.cyberduck.core.PasswordStoreFactory;
29 import ch.cyberduck.core.Path;
30 import ch.cyberduck.core.ProgressListener;
31 import ch.cyberduck.core.Session;
32 import ch.cyberduck.core.TranscriptListener;
33 import ch.cyberduck.core.exception.BackgroundException;
34 import ch.cyberduck.core.exception.ConnectionCanceledException;
35 import ch.cyberduck.core.preferences.PreferencesFactory;
37 import org.apache.log4j.Logger;
39 /**
40 * @version $Id$
42 public abstract class SessionBackgroundAction<T> extends AbstractBackgroundAction<T>
43 implements ProgressListener, TranscriptListener {
44 private static final Logger log = Logger.getLogger(SessionBackgroundAction.class);
46 /**
47 * Contains all exceptions thrown while this action was running
49 private BackgroundException exception;
51 /**
52 * This action encountered one or more exceptions
54 private boolean failed;
56 /**
57 * Contains the transcript of the session while this action was running
59 private StringBuilder transcript
60 = new StringBuilder();
62 /**
63 * The number of times this action has been run
65 protected int repeat = 0;
67 private static final String LINE_SEPARATOR
68 = System.getProperty("line.separator");
70 private AlertCallback alert;
72 private ProgressListener progressListener;
74 private TranscriptListener transcriptListener;
76 protected ConnectionService connection;
78 private final FailureDiagnostics<Exception> diagnostics
79 = new DefaultFailureDiagnostics();
81 protected Session<?> session;
83 private Cache<Path> cache;
85 public SessionBackgroundAction(final Session<?> session,
86 final Cache<Path> cache,
87 final AlertCallback alert,
88 final ProgressListener progress,
89 final TranscriptListener transcript,
90 final LoginCallback prompt,
91 final HostKeyCallback key) {
92 this(new LoginConnectionService(prompt, key, PasswordStoreFactory.get(),
93 progress, transcript), session, cache, alert, progress, transcript);
96 public SessionBackgroundAction(final LoginService login,
97 final Session<?> session,
98 final Cache<Path> cache,
99 final AlertCallback alert,
100 final ProgressListener progress,
101 final TranscriptListener transcript,
102 final HostKeyCallback key) {
103 this(new LoginConnectionService(login, key, progress, transcript), session, cache, alert, progress, transcript);
106 public SessionBackgroundAction(final ConnectionService connection,
107 final Session<?> session,
108 final Cache<Path> cache,
109 final AlertCallback alert,
110 final ProgressListener progress,
111 final TranscriptListener transcript) {
112 this.connection = connection;
113 this.session = session;
114 this.cache = cache;
115 this.alert = alert;
116 this.progressListener = progress;
117 this.transcriptListener = transcript;
120 public BackgroundException getException() {
121 return exception;
124 @Override
125 public void message(final String message) {
126 progressListener.message(message);
130 * Append to the transcript and notify listeners.
132 @Override
133 public void log(final boolean request, final String message) {
134 transcript.append(message).append(LINE_SEPARATOR);
135 transcriptListener.log(request, message);
138 @Override
139 public void prepare() throws ConnectionCanceledException {
140 super.prepare();
141 this.message(this.getActivity());
144 @Override
145 public void cancel() {
146 connection.cancel();
147 super.cancel();
151 * The number of times a new connection attempt should be made. Takes into
152 * account the number of times already tried.
154 * @return Greater than zero if a failed action should be repeated again
156 protected int retry() {
157 // The initial connection attempt does not count
158 return PreferencesFactory.get().getInteger("connection.retry") - repeat;
161 protected void reset() throws BackgroundException {
162 // Clear the transcript and exceptions
163 transcript = new StringBuilder();
164 // Reset the failure status but remember the previous exception for automatic retry.
165 failed = false;
169 * @return True if the the action had a permanent failures. Returns false if
170 * there were only temporary exceptions and the action succeeded upon retry
172 public boolean hasFailed() {
173 return failed;
176 @Override
177 public T call() {
178 try {
179 // Reset status
180 this.reset();
181 // Open connection
182 this.connect(session);
183 // Run action
184 return super.call();
186 catch(ConnectionCanceledException failure) {
187 // Do not report as failed if instanceof ConnectionCanceledException
188 log.warn(String.format("Connection canceled %s", failure.getMessage()));
190 catch(BackgroundException failure) {
191 log.warn(String.format("Failure executing background action: %s", failure));
192 exception = failure;
193 failed = true;
194 if(diagnostics.determine(failure) == FailureDiagnostics.Type.network) {
195 if(this.retry() > 0) {
196 if(log.isInfoEnabled()) {
197 log.info(String.format("Retry failed background action %s", this));
199 // This is an automated retry. Wait some time first.
200 this.pause();
201 if(!this.isCanceled()) {
202 repeat++;
203 // Re-run the action with the previous lock used
204 this.call();
209 return null;
212 protected boolean connect(final Session session) throws BackgroundException {
213 if(connection.check(session, cache, exception)) {
214 return true;
216 // Use existing connection
217 return false;
220 protected void close(final Session session) throws BackgroundException {
221 session.close();
222 cache.clear();
225 @Override
226 public boolean alert() {
227 if(this.hasFailed() && !this.isCanceled()) {
228 // Display alert if the action was not canceled intentionally
229 return alert.alert(session.getHost(), exception, transcript);
231 return false;
234 @Override
235 public void cleanup() {
236 this.message(null);
240 * Idle this action for some time. Blocks the caller.
242 public void pause() {
243 final BackgroundActionPauser pauser = new BackgroundActionPauser(this);
244 pauser.await(this);
247 @Override
248 public String getName() {
249 return BookmarkNameProvider.toString(session.getHost());
253 * @return The session instance
255 @Override
256 public Object lock() {
257 return session;
260 @Override
261 public String toString() {
262 final StringBuilder sb = new StringBuilder("SessionBackgroundAction{");
263 sb.append("session=").append(session);
264 sb.append(", failed=").append(failed);
265 sb.append(", exception=").append(exception);
266 sb.append('}');
267 return sb.toString();