/*
* Copyright 2012 Evernote Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.evernote.client.android;
import android.util.Log;
import com.evernote.edam.userstore.BootstrapInfo;
import com.evernote.edam.userstore.BootstrapProfile;
import com.evernote.thrift.TException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
/**
* A class that provides access to check the current
* {@link com.evernote.edam.userstore.Constants#EDAM_VERSION_MAJOR} and
* the {@link com.evernote.edam.userstore.Constants#EDAM_VERSION_MINOR} against the Evernote Web serivice for API
* Compatibility
*
* It provides access to the {@link List} of {@link BootstrapProfile} representing the possible server connections
* for the user. This list must be requested from the server on any type of authentication attempt
*
* @author @tylersmithnet
*/
public class BootstrapManager {
private static final String LOGTAG = "EvernoteSession";
/**
* List of locales that match china
*/
private static List<Locale> sChinaLocales = Arrays.asList(new Locale[]{
Locale.TRADITIONAL_CHINESE,
Locale.CHINESE,
Locale.CHINA,
Locale.SIMPLIFIED_CHINESE});
/**
* Server matched name for BootstrapProfile that matches china
*/
public static final String CHINA_PROFILE = "Evernote-China";
/**
* Display names for Yinxiang and Evernote
*/
public static final String DISPLAY_YXBIJI = "印象笔记";
public static final String DISPLAY_EVERNOTE = "Evernote";
public static final String DISPLAY_EVERNOTE_INTL = "Evernote International";
private ArrayList<String> mBootstrapServerUrls = new ArrayList<String>();
private AsyncUserStoreClient mUserStoreClient;
private Locale mLocale;
private ClientFactory mClientProducer;
private String mBootstrapServerUsed;
/**
* private constructor
*/
private BootstrapManager() {};
/**
* package-scope constructor
*/
BootstrapManager(EvernoteSession.EvernoteService service, ClientFactory producer) {
this(service, producer, Locale.getDefault());
}
/**
* package-scope constructor
*
* @param service {@link com.evernote.client.android.EvernoteSession.EvernoteService#PRODUCTION} when using
* production and {@link com.evernote.client.android.EvernoteSession.EvernoteService#SANDBOX} when using sandbox
* @param producer Client producer used to create clients
* @param locale Used to detect if the china servers need to be checked
*/
BootstrapManager(EvernoteSession.EvernoteService service, ClientFactory producer, Locale locale) {
mLocale = locale;
mClientProducer = producer;
mBootstrapServerUrls.clear();
switch(service) {
case PRODUCTION:
if (sChinaLocales.contains(mLocale)) {
mBootstrapServerUrls.add(EvernoteSession.HOST_CHINA);
}
mBootstrapServerUrls.add(EvernoteSession.HOST_PRODUCTION);
break;
case SANDBOX:
mBootstrapServerUrls.add(EvernoteSession.HOST_SANDBOX);
break;
}
}
/**
* Initialized the User Store to check for supported version of the API
*
* @throws ClientUnsupportedException on unsupported version
* @throws Exception on generic errors
*/
private void initializeUserStoreAndCheckVersion() throws Exception {
int i = 0;
String version = com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR + "."
+ com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR;
for (String url : mBootstrapServerUrls) {
i++;
try {
mUserStoreClient = mClientProducer.createUserStoreClient(url);
if (!mUserStoreClient.getClient().checkVersion(mClientProducer.getUserAgent(),
com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR,
com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR)) {
mUserStoreClient = null;
throw new ClientUnsupportedException(version);
}
mBootstrapServerUsed = url;
return;
} catch (ClientUnsupportedException cue) {
Log.e(LOGTAG, "Invalid Version", cue);
throw cue;
} catch (Exception e) {
mUserStoreClient = null;
if (i < mBootstrapServerUrls.size()) {
Log.e(LOGTAG, "Error contacting bootstrap server=" + url, e);
continue;
} else {
throw e;
}
}
}
}
/**
* Makes a web request to get the latest bootstrap information
* This is a requirement during the oauth process
*
* @return {@link BootstrapInfoWrapper}
* @throws Exception
*/
BootstrapInfoWrapper getBootstrapInfo() throws Exception {
Log.d(LOGTAG, "getBootstrapInfo()");
BootstrapInfo bsInfo = null;
try {
if (mUserStoreClient == null) {
initializeUserStoreAndCheckVersion();
}
bsInfo = mUserStoreClient.getClient().getBootstrapInfo(mLocale.toString());
printBootstrapInfo(bsInfo);
} catch (ClientUnsupportedException cue) {
throw cue;
} catch (TException e) {
Log.e(LOGTAG, "error getting bootstrap info", e);
}
BootstrapInfoWrapper wrapper = new BootstrapInfoWrapper(mBootstrapServerUsed, bsInfo);
return wrapper;
}
/**
* Log the {@link BootstrapProfile} list
* @param bsInfo
*/
void printBootstrapInfo(BootstrapInfo bsInfo) {
if (bsInfo == null) return;
Log.d(LOGTAG, "printBootstrapInfo");
List<BootstrapProfile> profiles = bsInfo.getProfiles();
if (profiles != null) {
for (BootstrapProfile profile : profiles) {
Log.d(LOGTAG, profile.toString());
}
} else {
Log.d(LOGTAG, "Profiles are null");
}
}
/**
* Wrapper class to hold the Evernote API server URL and the {@link BootstrapProfile} object
*/
static class BootstrapInfoWrapper {
private String mServerUrl;
private BootstrapInfo mBootstrapInfo;
BootstrapInfoWrapper(String serverUrl, BootstrapInfo info) {
mServerUrl = serverUrl;
mBootstrapInfo = info;
}
String getServerUrl() {
return mServerUrl;
}
BootstrapInfo getBootstrapInfo() {
return mBootstrapInfo;
}
}
public static class ClientUnsupportedException extends Exception {
public ClientUnsupportedException(String version) {
super("Client version " + version + " not supported.");
}
};
}