com.socialize.provider.BaseSocializeProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.socialize.provider.BaseSocializeProvider.java

Source

/*
 * Copyright (c) 2012 Socialize Inc. 
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.socialize.provider;

import android.content.Context;
import com.socialize.android.ioc.IBeanFactory;
import com.socialize.api.*;
import com.socialize.api.action.ActionType;
import com.socialize.auth.*;
import com.socialize.config.SocializeConfig;
import com.socialize.entity.*;
import com.socialize.error.SocializeApiError;
import com.socialize.error.SocializeException;
import com.socialize.log.SocializeLogger;
import com.socialize.net.HttpClientFactory;
import com.socialize.util.HttpUtils;
import com.socialize.util.IOUtils;
import com.socialize.util.JSONParser;
import com.socialize.util.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.Map.Entry;

/**
 * @author Jason Polites
 * 
 * @param <T>
 */
public abstract class BaseSocializeProvider<T extends SocializeObject> implements SocializeProvider<T> {

    public static final String JSON_ATTR_ERRORS = "errors";
    public static final String JSON_ATTR_ITEMS = "items";
    public static final String JSON_ATTR_COUNT = "total_count";

    private SocializeObjectFactory<User> userFactory;
    private IBeanFactory<AuthProviderData> authProviderDataFactory;
    private AuthProviderInfoBuilder authProviderInfoBuilder;
    private ErrorFactory errorFactory;
    private HttpClientFactory clientFactory;
    private SocializeSessionFactory sessionFactory;
    private SocializeRequestFactory<T> requestFactory;
    private JSONParser jsonParser;
    private SocializeLogger logger;
    private HttpUtils httpUtils;
    private IOUtils ioUtils;
    private SocializeSessionPersister sessionPersister;
    private SocializeConfig config;
    private WeakReference<Context> context;

    public BaseSocializeProvider() {
        super();
    }

    @Override
    public void init(Context context) {
        this.context = new WeakReference<Context>(context);
    }

    public void setUserFactory(SocializeObjectFactory<User> userFactory) {
        this.userFactory = userFactory;
    }

    public void setClientFactory(HttpClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }

    public void setRequestFactory(SocializeRequestFactory<T> requestFactory) {
        this.requestFactory = requestFactory;
    }

    public void setJsonParser(JSONParser jsonParser) {
        this.jsonParser = jsonParser;
    }

    public void setHttpUtils(HttpUtils httpUtils) {
        this.httpUtils = httpUtils;
    }

    public void setIoUtils(IOUtils ioUtils) {
        this.ioUtils = ioUtils;
    }

    public void setConfig(SocializeConfig config) {
        this.config = config;
    }

    public void setErrorFactory(ErrorFactory errorFactory) {
        this.errorFactory = errorFactory;
    }

    public void setSessionFactory(SocializeSessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public SocializeSession authenticate(String endpoint, String key, String secret, String uuid,
            String advertiserId) throws SocializeException {

        if (authProviderDataFactory == null) {
            throw new SocializeException("Socialize not initialized");
        }

        AuthProviderData data = authProviderDataFactory.getBean();

        if (data == null) {
            throw new SocializeException("Socialize not initialized");
        }

        data.setAuthProviderInfo(
                authProviderInfoBuilder.getFactory(AuthProviderType.SOCIALIZE).getInstanceForRead());
        return authenticate(endpoint, key, secret, data, uuid, advertiserId);
    }

    @Override
    public WritableSession loadSession(String endpoint, String key, String secret) throws SocializeException {

        if (sessionPersister != null) {
            WritableSession loaded = sessionPersister.load(context.get());

            // Verify that the key/secret matches
            if (loaded != null) {

                String loadedKey = loaded.getConsumerKey();
                String loadedSecret = loaded.getConsumerSecret();
                String loadedHost = loaded.getHost();

                String host = config.getProperty(SocializeConfig.API_HOST);

                if (loadedKey != null && loadedKey.equals(key) && loadedSecret != null
                        && loadedSecret.equals(secret) && loadedHost != null && loadedHost.equals(host)) {

                    return loaded;
                }
            }
        }
        return null;
    }

    @Override
    public boolean validateSession(SocializeSession session, AuthProviderData data) {
        AuthProviderInfo info = data.getAuthProviderInfo();
        if (info != null) {
            if (info.getType().equals(AuthProviderType.SOCIALIZE)) {
                return true;
            }
            return validateSessionAuthData(session, data, info);
        } else {
            return false;
        }
    }

    public boolean validateSessionAuthData(SocializeSession loaded, AuthProviderData data, AuthProviderInfo info) {
        UserProviderCredentialsMap userProviderCredentialsMap = loaded.getUserProviderCredentials();
        if (userProviderCredentialsMap != null) {
            UserProviderCredentials userProviderCredentials = userProviderCredentialsMap.get(info.getType());
            if (userProviderCredentials != null && userProviderCredentials.getAuthProviderInfo().matches(info)) {
                boolean ok = true;

                String token3rdParty = data.getToken3rdParty();
                String secret3rdParty = data.getSecret3rdParty();

                if (!StringUtils.isEmpty(token3rdParty)) {
                    ok = userProviderCredentials.getAccessToken().equals(token3rdParty);
                }

                if (ok && !StringUtils.isEmpty(secret3rdParty)) {
                    ok = userProviderCredentials.getTokenSecret().equals(secret3rdParty);
                }

                return ok;
            }
        }
        return false;
    }

    // Mockable
    protected AuthProviderData newAuthProviderData() {
        return new AuthProviderData();
    }

    @Override
    public void clearSession(AuthProviderType type) {
        if (sessionPersister != null) {
            sessionPersister.delete(context.get(), type);
        }
    }

    @Override
    public void clearSession() {
        if (sessionPersister != null) {
            sessionPersister.delete(context.get());
        }
    }

    public void saveSession(SocializeSession session) {
        if (sessionPersister != null) {
            sessionPersister.save(context.get(), session);
        }
    }

    @Override
    public SocializeSession authenticate(String endpoint, String key, String secret, AuthProviderData data,
            String uuid, String advertiserId) throws SocializeException {
        try {
            SessionLock.lock();

            WritableSession session = loadSession(endpoint, key, secret);

            if (session != null) {

                if (validateSession(session, data)) {
                    return session;
                } else {
                    session = setProviderCredentialsForUser(data, session);
                }
            }

            if (session == null) {
                session = sessionFactory.create(key, secret, data);
            }

            endpoint = prepareEndpoint(session, endpoint, true);

            if (!clientFactory.isDestroyed()) {

                HttpClient client = clientFactory.getClient();

                HttpEntity entity = null;

                try {
                    HttpUriRequest request = requestFactory.getAuthRequest(session, endpoint, uuid, advertiserId,
                            data);

                    if (logger != null && logger.isDebugEnabled()) {
                        logger.debug("Calling authenticate endpoint for device [" + uuid + "]");
                    }

                    HttpResponse response = executeRequest(client, request);

                    if (logger != null && logger.isDebugEnabled()) {
                        logger.debug("RESPONSE CODE: " + response.getStatusLine().getStatusCode());
                    }

                    entity = response.getEntity();

                    if (httpUtils.isHttpError(response)) {

                        if (sessionPersister != null && httpUtils.isAuthError(response)) {
                            sessionPersister.delete(context.get());
                        }

                        String msg = ioUtils.readSafe(entity.getContent());

                        throw new SocializeApiError(httpUtils, response.getStatusLine().getStatusCode(), msg);
                    } else {

                        String responseData = ioUtils.readSafe(entity.getContent());

                        if (logger != null && logger.isDebugEnabled()) {
                            logger.debug("RESPONSE: " + responseData);
                        }

                        JSONObject json = jsonParser.parseObject(responseData);

                        User user = userFactory.fromJSON(json.getJSONObject("user"));

                        String oauth_token = json.getString("oauth_token");
                        String oauth_token_secret = json.getString("oauth_token_secret");

                        if (StringUtils.isEmpty(oauth_token)) {
                            throw new SocializeException("oauth_token was empty in response from server");
                        }

                        if (StringUtils.isEmpty(oauth_token_secret)) {
                            throw new SocializeException("oauth_token_secret was empty in response from server");
                        }

                        session.setConsumerToken(oauth_token);
                        session.setConsumerTokenSecret(oauth_token_secret);
                        session.setUser(user);

                        setProviderCredentialsForUser(data, session);

                        // Ensure the user credentials match the user auth data returned from the server
                        verifyProviderCredentialsForUser(session, user);

                        saveSession(session);
                    }
                } catch (Exception e) {
                    throw SocializeException.wrap(e);
                } finally {
                    closeEntity(entity);
                }
            } else {
                if (logger != null) {
                    logger.warn("Attempt to access HttpClientFactory that was already destroyed");
                }
            }

            return session;
        } finally {
            SessionLock.unlock();
        }
    }

    protected WritableSession setProviderCredentialsForUser(AuthProviderData data, WritableSession session) {
        AuthProviderInfo info = data.getAuthProviderInfo();

        if (info != null) {
            DefaultUserProviderCredentials userProviderCredentials = new DefaultUserProviderCredentials();
            userProviderCredentials.setAccessToken(data.getToken3rdParty());
            userProviderCredentials.setTokenSecret(data.getSecret3rdParty());
            userProviderCredentials.setUserId(data.getUserId3rdParty());

            UserProviderCredentials current = session.getUserProviderCredentials().get(info.getType());

            if (current != null) {
                AuthProviderInfo authProviderInfo = current.getAuthProviderInfo();

                if (authProviderInfo != null) {
                    authProviderInfo.merge(data.getAuthProviderInfo());
                }

                userProviderCredentials.setAuthProviderInfo(authProviderInfo);
            } else {
                userProviderCredentials.setAuthProviderInfo(data.getAuthProviderInfo());
            }

            session.getUserProviderCredentials().put(info.getType(), userProviderCredentials);
        } else {
            // Legacy
            session = null;
        }

        return session;
    }

    protected void verifyProviderCredentialsForUser(WritableSession session, User user) {
        List<UserAuthData> authData = user.getAuthData();
        UserProviderCredentialsMap credentials = session.getUserProviderCredentials();

        if (credentials != null) {
            if (authData != null) {
                Map<AuthProviderType, UserProviderCredentials> validCreds = new LinkedHashMap<AuthProviderType, UserProviderCredentials>();
                for (UserAuthData userAuthData : authData) {
                    UserProviderCredentials creds = credentials.get(userAuthData.getAuthProviderType());
                    if (creds != null) {
                        validCreds.put(userAuthData.getAuthProviderType(), creds);
                    }
                }

                // Clear and reset
                credentials.removeAll();

                Set<Entry<AuthProviderType, UserProviderCredentials>> entrySet = validCreds.entrySet();

                for (Entry<AuthProviderType, UserProviderCredentials> entry : entrySet) {
                    credentials.put(entry.getKey(), entry.getValue());
                }
            } else {
                credentials.removeAll();
            }

            // Set back to session
            session.setUserProviderCredentials(credentials);
        }
    }

    @Override
    public T get(SocializeSession session, String endpoint, String id, ActionType type) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest get = requestFactory.getGetRequest(session, endpoint, id);
        return doGetTypeRequest(get, type);
    }

    @Override
    public T get(SocializeSession session, String endpoint, String id) throws SocializeException {
        return get(session, endpoint, id, ActionType.UNKNOWN);
    }

    @Override
    public void delete(SocializeSession session, String endpoint, String id) throws SocializeException {
        HttpEntity entity = null;

        if (!clientFactory.isDestroyed()) {
            try {
                endpoint = prepareEndpoint(session, endpoint);

                HttpClient client = clientFactory.getClient();

                HttpUriRequest del = requestFactory.getDeleteRequest(session, endpoint, id);

                HttpResponse response = client.execute(del);

                entity = response.getEntity();

                if (httpUtils.isHttpError(response)) {

                    if (sessionPersister != null && httpUtils.isAuthError(response)) {
                        sessionPersister.delete(context.get());
                    }

                    String msg = ioUtils.readSafe(entity.getContent());
                    throw new SocializeApiError(httpUtils, response.getStatusLine().getStatusCode(), msg);
                }
            } catch (Exception e) {
                throw SocializeException.wrap(e);
            } finally {
                closeEntity(entity);
            }
        } else {
            if (logger != null) {
                logger.warn("Attempt to access HttpClientFactory that was already destroyed");
            }
        }
    }

    @Override
    public ListResult<T> list(SocializeSession session, String endpoint, int startIndex, int endIndex)
            throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getListRequest(session, endpoint, startIndex, endIndex);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> list(SocializeSession session, String endpoint, String key, String[] ids, String idKey,
            Map<String, String> extraParams, int startIndex, int endIndex) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getListRequest(session, endpoint, key, ids, idKey, extraParams,
                startIndex, endIndex);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> list(SocializeSession session, String endpoint, String key, String[] ids, int startIndex,
            int endIndex) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getListRequest(session, endpoint, key, ids, startIndex, endIndex);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> put(SocializeSession session, String endpoint, T object) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getPutRequest(session, endpoint, object);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> put(SocializeSession session, String endpoint, Collection<T> objects)
            throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getPutRequest(session, endpoint, objects);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> post(SocializeSession session, String endpoint, T object, boolean jsonResponse)
            throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getPostRequest(session, endpoint, object);
        return doListTypeRequest(request, ActionType.UNKNOWN, jsonResponse);
    }

    @Override
    public T putAsPost(SocializeSession session, String endpoint, T object) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getPostRequest(session, endpoint, object);
        return doGetTypeRequest(request, ActionType.UNKNOWN);
    }

    @Override
    public ListResult<T> post(SocializeSession session, String endpoint, Collection<T> objects,
            boolean isJSONResponse) throws SocializeException {
        endpoint = prepareEndpoint(session, endpoint);
        HttpUriRequest request = requestFactory.getPostRequest(session, endpoint, objects);
        return doListTypeRequest(request, ActionType.UNKNOWN);
    }

    private T doGetTypeRequest(HttpUriRequest request, ActionType actionType) throws SocializeException {
        HttpEntity entity = null;

        if (!clientFactory.isDestroyed()) {

            try {

                HttpClient client = clientFactory.getClient();

                HttpResponse response = executeRequest(client, request);

                if (logger != null && logger.isDebugEnabled()) {
                    logger.debug("RESPONSE CODE: " + response.getStatusLine().getStatusCode());
                }

                entity = response.getEntity();

                if (httpUtils.isHttpError(response)) {

                    if (sessionPersister != null && httpUtils.isAuthError(response)) {
                        sessionPersister.delete(context.get());
                    }

                    String msg = ioUtils.readSafe(entity.getContent());
                    throw new SocializeApiError(httpUtils, response.getStatusLine().getStatusCode(), msg);
                } else {
                    String responseData = ioUtils.readSafe(entity.getContent());

                    if (logger != null && logger.isDebugEnabled()) {
                        logger.debug("RESPONSE: " + responseData);
                    }

                    JSONObject json = jsonParser.parseObject(responseData);

                    return fromJSON(json, actionType);
                }
            } catch (Exception e) {
                throw SocializeException.wrap(e);
            } finally {
                closeEntity(entity);
            }
        } else {
            if (logger != null) {
                logger.warn("Attempt to access HttpClientFactory that was already destroyed");
            }

            return null;
        }
    }

    private HttpResponse executeRequest(HttpClient client, HttpUriRequest request) throws IOException {

        if (logger != null && logger.isDebugEnabled()) {

            StringBuilder builder = new StringBuilder();
            Header[] allHeaders = request.getAllHeaders();

            for (Header header : allHeaders) {
                builder.append(header.getName());
                builder.append(":");
                builder.append(header.getValue());
                builder.append("\n");
            }

            if (logger.isDebugEnabled()) {
                logger.debug(
                        "REQUEST \nurl:[" + request.getURI().toString() + "] \nheaders:\n" + builder.toString());

            }

            if (request instanceof HttpPost) {
                HttpPost post = (HttpPost) request;
                HttpEntity entity = post.getEntity();
                String requestData = ioUtils.readSafe(entity.getContent());

                if (logger.isDebugEnabled()) {
                    logger.debug("REQUEST \ndata:[" + requestData + "]");
                }
            }
        }

        return client.execute(request);
    }

    private ListResult<T> doListTypeRequest(HttpUriRequest request, ActionType type) throws SocializeException {
        return doListTypeRequest(request, type, true);
    }

    private ListResult<T> doListTypeRequest(HttpUriRequest request, ActionType type, boolean isJSONResponse)
            throws SocializeException {
        List<T> results = null;
        List<ActionError> errors = null;
        HttpEntity entity = null;

        ListResult<T> result = null;

        if (!clientFactory.isDestroyed()) {

            try {
                HttpClient client = clientFactory.getClient();

                if (logger != null && logger.isDebugEnabled()) {
                    logger.debug("Request: " + request.getMethod() + " " + request.getRequestLine().getUri());
                }

                HttpResponse response = executeRequest(client, request);

                if (logger != null && logger.isDebugEnabled()) {
                    logger.debug("RESPONSE CODE: " + response.getStatusLine().getStatusCode());
                }

                entity = response.getEntity();

                if (httpUtils.isHttpError(response)) {

                    if (sessionPersister != null && httpUtils.isAuthError(response)) {
                        sessionPersister.delete(context.get());
                    }

                    String msg = ioUtils.readSafe(entity.getContent());
                    throw new SocializeApiError(httpUtils, response.getStatusLine().getStatusCode(), msg);
                } else {

                    result = new ListResult<T>();

                    if (isJSONResponse) {
                        // Read the json just for logging
                        String json = ioUtils.readSafe(entity.getContent());

                        if (logger != null && logger.isDebugEnabled()) {
                            logger.debug("RESPONSE: " + json);
                        }

                        if (!StringUtils.isEmpty(json)) {

                            JSONObject object;

                            try {
                                object = jsonParser.parseObject(json);
                            } catch (JSONException je) {
                                throw new SocializeException("Failed to parse response as JSON [" + json + "]", je);
                            }

                            if (object.has(JSON_ATTR_ERRORS) && !object.isNull(JSON_ATTR_ERRORS)) {

                                JSONArray errorList = object.getJSONArray(JSON_ATTR_ERRORS);

                                int length = errorList.length();

                                errors = new ArrayList<ActionError>(length);

                                for (int i = 0; i < length; i++) {
                                    JSONObject jsonObject = errorList.getJSONObject(i);
                                    ActionError error = errorFactory.fromJSON(jsonObject);
                                    errors.add(error);
                                }

                                result.setErrors(errors);
                            }

                            if (object.has(JSON_ATTR_ITEMS) && !object.isNull(JSON_ATTR_ITEMS)) {
                                JSONArray list = object.getJSONArray(JSON_ATTR_ITEMS);

                                int length = list.length();

                                results = new ArrayList<T>(length);

                                for (int i = 0; i < length; i++) {
                                    results.add(fromJSON(list.getJSONObject(i), type));
                                }

                                result.setItems(results);
                            }

                            if (object.has(JSON_ATTR_COUNT) && !object.isNull(JSON_ATTR_COUNT)) {
                                result.setTotalCount(object.getInt(JSON_ATTR_COUNT));
                            }
                        }
                    }
                }
            } catch (Throwable e) {
                throw SocializeException.wrap(e);
            } finally {
                closeEntity(entity);
            }

            return result;
        } else {
            if (logger != null) {
                logger.warn("Attempt to access HttpClientFactory that was already destroyed");
            }

            return null;
        }
    }

    public void setLogger(SocializeLogger logger) {
        this.logger = logger;
    }

    public void setSessionPersister(SocializeSessionPersister sessionPersister) {
        this.sessionPersister = sessionPersister;
    }

    public void setAuthProviderDataFactory(IBeanFactory<AuthProviderData> authProviderDataFactory) {
        this.authProviderDataFactory = authProviderDataFactory;
    }

    public void setAuthProviderInfoBuilder(AuthProviderInfoBuilder authProviderInfoBuilder) {
        this.authProviderInfoBuilder = authProviderInfoBuilder;
    }

    private final String prepareEndpoint(SocializeSession session, String endpoint) {
        return prepareEndpoint(session, endpoint, false);
    }

    private final String prepareEndpoint(SocializeSession session, String endpoint, boolean secure) {
        return prepareEndpoint(session.getHost(), endpoint, secure);
    }

    private final String prepareEndpoint(String host, String endpoint, boolean secure) {
        endpoint = endpoint.trim();

        if (StringUtils.isEmpty(host)) {
            logger.warn("The session did not have a host configured, using the config");
            host = config.getProperty(SocializeConfig.API_HOST);
        }

        if (host != null) {

            if (!host.startsWith("http")) {
                if (secure) {
                    host = "https://" + host;
                } else {
                    host = "http://" + host;
                }
            }

            if (!host.endsWith("/")) {
                if (!endpoint.startsWith("/")) {
                    host += "/";
                }
            } else if (endpoint.startsWith("/")) {
                endpoint = endpoint.substring(1, endpoint.length());
            }

            endpoint = host + endpoint;
        } else {
            logger.error("Could not locate host property in session or config!");
        }

        if (!endpoint.endsWith("/")) {
            endpoint += "/";
        }

        return endpoint;
    }

    private final void closeEntity(HttpEntity entity) {
        if (entity != null) {
            try {
                entity.consumeContent();
            } catch (IOException e) {
                if (logger != null) {
                    logger.warn("Failed to fully consume http response content", e);
                }
            }
        }
    }

    public SocializeSessionPersister getSessionPersister() {
        return sessionPersister;
    }

    public abstract T fromJSON(JSONObject json, ActionType type) throws JSONException;
}