RemoteOperation.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /* ownCloud Android client application
  2. * Copyright (C) 2012-2013 ownCloud Inc.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. package com.owncloud.android.operations;
  18. import java.io.IOException;
  19. import org.apache.commons.httpclient.Credentials;
  20. import com.owncloud.android.Log_OC;
  21. import com.owncloud.android.MainApp;
  22. import com.owncloud.android.network.BearerCredentials;
  23. import com.owncloud.android.network.OwnCloudClientUtils;
  24. import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  25. import android.accounts.Account;
  26. import android.accounts.AccountManager;
  27. import android.accounts.AccountsException;
  28. import android.app.Activity;
  29. import android.content.Context;
  30. import android.os.Handler;
  31. import eu.alefzero.webdav.WebdavClient;
  32. /**
  33. * Operation which execution involves one or several interactions with an ownCloud server.
  34. *
  35. * Provides methods to execute the operation both synchronously or asynchronously.
  36. *
  37. * @author David A. Velasco
  38. */
  39. public abstract class RemoteOperation implements Runnable {
  40. private static final String TAG = RemoteOperation.class.getSimpleName();
  41. /** ownCloud account in the remote ownCloud server to operate */
  42. private Account mAccount = null;
  43. /** Android Application context */
  44. private Context mContext = null;
  45. /** Object to interact with the remote server */
  46. private WebdavClient mClient = null;
  47. /** Callback object to notify about the execution of the remote operation */
  48. private OnRemoteOperationListener mListener = null;
  49. /** Handler to the thread where mListener methods will be called */
  50. private Handler mListenerHandler = null;
  51. /** Activity */
  52. private Activity mCallerActivity;
  53. /**
  54. * Abstract method to implement the operation in derived classes.
  55. */
  56. protected abstract RemoteOperationResult run(WebdavClient client);
  57. /**
  58. * Synchronously executes the remote operation on the received ownCloud account.
  59. *
  60. * Do not call this method from the main thread.
  61. *
  62. * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
  63. *
  64. * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
  65. * @param context Android context for the component calling the method.
  66. * @return Result of the operation.
  67. */
  68. public final RemoteOperationResult execute(Account account, Context context) {
  69. if (account == null)
  70. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
  71. if (context == null)
  72. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
  73. mAccount = account;
  74. mContext = context.getApplicationContext();
  75. try {
  76. mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
  77. } catch (Exception e) {
  78. Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
  79. return new RemoteOperationResult(e);
  80. }
  81. return run(mClient);
  82. }
  83. /**
  84. * Synchronously executes the remote operation
  85. *
  86. * Do not call this method from the main thread.
  87. *
  88. * @param client Client object to reach an ownCloud server during the execution of the operation.
  89. * @return Result of the operation.
  90. */
  91. public final RemoteOperationResult execute(WebdavClient client) {
  92. if (client == null)
  93. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
  94. mClient = client;
  95. return run(client);
  96. }
  97. /**
  98. * Asynchronously executes the remote operation
  99. *
  100. * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
  101. *
  102. * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
  103. * @param context Android context for the component calling the method.
  104. * @param listener Listener to be notified about the execution of the operation.
  105. * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
  106. * @return Thread were the remote operation is executed.
  107. */
  108. public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
  109. if (account == null)
  110. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
  111. if (context == null)
  112. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
  113. mAccount = account;
  114. mContext = context.getApplicationContext();
  115. mCallerActivity = callerActivity;
  116. mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
  117. mListener = listener;
  118. mListenerHandler = listenerHandler;
  119. Thread runnerThread = new Thread(this);
  120. runnerThread.start();
  121. return runnerThread;
  122. }
  123. /**
  124. * Asynchronously executes the remote operation
  125. *
  126. * @param client Client object to reach an ownCloud server during the execution of the operation.
  127. * @param listener Listener to be notified about the execution of the operation.
  128. * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
  129. * @return Thread were the remote operation is executed.
  130. */
  131. public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
  132. if (client == null) {
  133. throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
  134. }
  135. mClient = client;
  136. if (listener == null) {
  137. throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
  138. }
  139. mListener = listener;
  140. if (listenerHandler == null) {
  141. throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
  142. }
  143. mListenerHandler = listenerHandler;
  144. Thread runnerThread = new Thread(this);
  145. runnerThread.start();
  146. return runnerThread;
  147. }
  148. /**
  149. * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
  150. *
  151. * @param listener Listener to be notified about the execution of the operation.
  152. * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
  153. * @return Thread were the remote operation is executed.
  154. */
  155. public final RemoteOperationResult retry() {
  156. return execute(mClient);
  157. }
  158. /**
  159. * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
  160. *
  161. * @param listener Listener to be notified about the execution of the operation.
  162. * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
  163. * @return Thread were the remote operation is executed.
  164. */
  165. public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
  166. return execute(mClient, listener, listenerHandler);
  167. }
  168. /**
  169. * Asynchronous execution of the operation
  170. * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)},
  171. * and result posting.
  172. *
  173. * TODO refactor && clean the code; now it's a mess
  174. */
  175. @Override
  176. public final void run() {
  177. RemoteOperationResult result = null;
  178. boolean repeat = false;
  179. do {
  180. try{
  181. if (mClient == null) {
  182. if (mAccount != null && mContext != null) {
  183. if (mCallerActivity != null) {
  184. mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
  185. } else {
  186. mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
  187. }
  188. } else {
  189. throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
  190. }
  191. }
  192. } catch (IOException e) {
  193. Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
  194. result = new RemoteOperationResult(e);
  195. } catch (AccountsException e) {
  196. Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
  197. result = new RemoteOperationResult(e);
  198. }
  199. if (result == null)
  200. result = run(mClient);
  201. repeat = false;
  202. if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
  203. // (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
  204. (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
  205. /// possible fail due to lack of authorization in an operation performed in foreground
  206. Credentials cred = mClient.getCredentials();
  207. String ssoSessionCookie = mClient.getSsoSessionCookie();
  208. if (cred != null || ssoSessionCookie != null) {
  209. /// confirmed : unauthorized operation
  210. AccountManager am = AccountManager.get(mContext);
  211. boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
  212. boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
  213. if (bearerAuthorization) {
  214. am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken());
  215. } else if (samlBasedSsoAuthorization ) {
  216. am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie);
  217. } else {
  218. am.clearPassword(mAccount);
  219. }
  220. mClient = null;
  221. repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
  222. result = null;
  223. }
  224. }
  225. } while (repeat);
  226. final RemoteOperationResult resultToSend = result;
  227. if (mListenerHandler != null && mListener != null) {
  228. mListenerHandler.post(new Runnable() {
  229. @Override
  230. public void run() {
  231. mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
  232. }
  233. });
  234. }
  235. }
  236. /**
  237. * Returns the current client instance to access the remote server.
  238. *
  239. * @return Current client instance to access the remote server.
  240. */
  241. public final WebdavClient getClient() {
  242. return mClient;
  243. }
  244. }