de.stadtrallye.rallyesoft.model.Server.java Source code

Java tutorial

Introduction

Here is the source code for de.stadtrallye.rallyesoft.model.Server.java

Source

/*
 * Copyright (c) 2014 Jakob Wenzel, Ramon Wirsch.
 *
 * This file is part of RallyeSoft.
 *
 * RallyeSoft is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * RallyeSoft is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RallyeSoft. If not, see <http://www.gnu.org/licenses/>.
 */

package de.stadtrallye.rallyesoft.model;

import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import de.rallye.model.structures.Group;
import de.rallye.model.structures.GroupUser;
import de.rallye.model.structures.LoginInfo;
import de.rallye.model.structures.ServerInfo;
import de.rallye.model.structures.ServerLogin;
import de.rallye.model.structures.UserAuth;
import de.stadtrallye.rallyesoft.exceptions.NoServerKnownException;
import de.stadtrallye.rallyesoft.model.chat.ChatManager;
import de.stadtrallye.rallyesoft.model.chat.IChatManager;
import de.stadtrallye.rallyesoft.model.map.IMapManager;
import de.stadtrallye.rallyesoft.model.map.MapManager;
import de.stadtrallye.rallyesoft.model.tasks.ITaskManager;
import de.stadtrallye.rallyesoft.model.tasks.TaskManager;
import de.stadtrallye.rallyesoft.net.AuthProvider;
import de.stadtrallye.rallyesoft.net.Paths;
import de.stadtrallye.rallyesoft.net.PictureIdResolver;
import de.stadtrallye.rallyesoft.net.retrofit.RetroAuthCommunicator;
import de.stadtrallye.rallyesoft.net.retrofit.RetroCommunicator;
import de.stadtrallye.rallyesoft.net.retrofit.RetroFactory;
import de.stadtrallye.rallyesoft.storage.Storage;
import de.stadtrallye.rallyesoft.util.converters.Serialization;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;

import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static de.stadtrallye.rallyesoft.storage.db.DatabaseHelper.Groups;
import static de.stadtrallye.rallyesoft.storage.db.DatabaseHelper.Users;

/**
 * Created by Ramon on 22.09.2014.
 */
@JsonAutoDetect(getterVisibility = NONE)
public class Server extends AuthProvider {

    private static final String THIS = Server.class.getSimpleName();
    private static final List<IServer.ICurrentServerListener> currentServerListeners = new ArrayList<>();
    private static Server currentServer;
    private static RetroFactory retroFactory = new RetroFactory();

    @JsonProperty
    private final String address;
    private final RetroFactory.ServerHandle serverHandle;
    private final RetroCommunicator communicator;
    private final List<IServer.IServerListener> listeners = new ArrayList<>();

    @JsonProperty
    private ServerInfo serverInfo;
    private RetroAuthCommunicator authCommunicator;
    private ITaskManager taskManager;
    private ChatManager chatManager;
    private MapManager mapManager;
    private List<Group> groups;

    private final ReadWriteLock serverLock = new ReentrantReadWriteLock();
    @JsonProperty
    private String userName;
    private boolean refreshingGroups;
    private boolean refreshingUsers;

    public Server(String address) {
        super();
        this.address = address;
        serverHandle = retroFactory.getServer(address, this);
        this.communicator = serverHandle.getPublicApi();
    }

    @JsonCreator
    public Server(@JsonProperty("address") String address, @JsonProperty("groupID") Integer groupID,
            @JsonProperty("userAuth") UserAuth userAuth, @JsonProperty("serverInfo") ServerInfo serverInfo,
            @JsonProperty("userName") String userName) {
        super(groupID, userAuth);
        this.address = address;
        this.userName = userName;
        serverHandle = retroFactory.getServer(address, this);
        this.communicator = serverHandle.getPublicApi();
        this.serverInfo = serverInfo;
    }

    public static Server getCurrentServer() {
        if (currentServer == null)
            load();

        return currentServer;
    }

    public static void setCurrentServer(Server server) {
        if (currentServer != null && currentServer.hasUserAuth())
            currentServer.tryLogout();

        currentServer = server;
        try {
            currentServer.save();// maybe async?
        } catch (IOException e) {
            Log.e(THIS, "Failed to save Server", e);
        }

        //TODO move this to the constructors of Manager's and make it conditional on sth. like Server.getCurrentServer().isChatInitialized() //requires additional saving to config, so to much work for now
        server.acquireChatManager(server).forceRefreshChatrooms();
        server.releaseChatManager(server);
        server.acquireTaskManager(server).forceRefresh();
        server.releaseTaskManager(server);

        notifyCurrentServerChanged();
    }

    public static Server load(String json) {
        if (json == null)
            return null;

        ObjectMapper mapper = Serialization.getJsonInstance();
        try {
            return mapper.readValue(json, Server.class);
        } catch (IOException e) {
            Log.e(THIS, "Failed to deserialize Server", e);
            return null;
        }
    }

    public static void load() {
        try {
            ObjectMapper mapper = Serialization.getJsonInstance();

            currentServer = mapper.readValue(Storage.getServerConfigInputStream(), Server.class);
        } catch (FileNotFoundException e) {
            Log.w(THIS, "No previously saved Server!");
        } catch (IOException e) {
            Log.e(THIS, "Cannot load Server", e);
        }
        //      notifyCurrentServerChanged();
    }

    private static void notifyCurrentServerChanged() {
        for (IServer.ICurrentServerListener l : currentServerListeners) {
            l.onNewCurrentServer(currentServer);
        }
    }

    @Deprecated
    public static boolean isStillCurrent(Server server, Object handle) {
        return server == currentServer;
    }

    /**
     * Not thread safe!!!
     * If you call this from somewhere else as the Android UI Thread, fix it!!!!!
     *
     * @param listener listener
     */
    public static void addListener(IServer.ICurrentServerListener listener) {
        currentServerListeners.add(listener);
    }

    public static void removeListener(IServer.ICurrentServerListener listener) {
        currentServerListeners.remove(listener);
    }

    public ServerInfo getServerInfoCached() {
        serverLock.readLock().lock();
        try {
            return serverInfo;
        } finally {
            serverLock.readLock().unlock();
        }
    }

    public void addListener(IServer.IServerListener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }
    }

    public void removeListener(IServer.IServerListener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }

    public void updateAvailableGroups() {
        synchronized (this) {
            if (refreshingGroups) {
                Log.w(THIS, "Preventing concurrent Group refreshes");
                return;
            }
            refreshingGroups = true;
        }

        communicator.getAvailableGroups(new Callback<List<Group>>() {
            @Override
            public void success(List<Group> groups, Response response) {
                Server.this.groups = groups;
                writeGroups();
                notifyAvailableGroups();
                if (chatManager != null) {
                    chatManager.onGroupsChanged();
                }

                synchronized (this) {
                    refreshingGroups = false;
                }
            }

            @Override
            public void failure(RetrofitError e) {
                //TODO Server.commError()
                Log.e(THIS, "unable to get Groups", e);

                synchronized (this) {
                    refreshingGroups = false;
                }
            }
        });
    }

    private void notifyAvailableGroups() {
        android.os.Handler handler;
        synchronized (listeners) {
            for (final IServer.IServerListener listener : listeners) {
                handler = listener.getCallbackHandler();
                if (handler != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            listener.onAvailableGroupsChanged(groups);
                        }
                    });
                } else {
                    listener.onAvailableGroupsChanged(groups);
                }
            }
        }
    }

    public void updateServerInfo() {
        communicator.getServerInfo(new Callback<ServerInfo>() {
            @Override
            public void success(ServerInfo serverInfo, Response response) {
                serverLock.writeLock().lock();
                try {
                    Server.this.serverInfo = serverInfo;
                } finally {
                    serverLock.writeLock().unlock();
                }
                notifyServerInfo();
            }

            @Override
            public void failure(RetrofitError e) {
                //TODO Server.commError()
                Log.e(THIS, "unable to get ServerInfo", e);
            }
        });
    }

    private void notifyServerInfo() {
        android.os.Handler handler;
        synchronized (listeners) {
            serverLock.readLock().lock();
            try {
                for (final IServer.IServerListener listener : listeners) {
                    handler = listener.getCallbackHandler();
                    if (handler != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                serverLock.readLock().lock();
                                try {
                                    listener.onServerInfoChanged(serverInfo);
                                } finally {
                                    serverLock.readLock().unlock();
                                }
                            }
                        });
                    } else {
                        listener.onServerInfoChanged(serverInfo);
                    }
                }
            } finally {
                serverLock.readLock().unlock();
            }
        }
    }

    private void notifyConnectionFailed(final Exception e, final int status) {
        android.os.Handler handler;
        synchronized (listeners) {
            for (final IServer.IServerListener listener : listeners) {
                handler = listener.getCallbackHandler();
                if (handler != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            listener.onConnectionFailed(e, status);
                        }
                    });
                } else {
                    listener.onConnectionFailed(e, status);
                }
            }
        }
    }

    private void notifyLoginSuccessful() {
        android.os.Handler handler;
        synchronized (listeners) {
            for (final IServer.IServerListener listener : listeners) {
                handler = listener.getCallbackHandler();
                if (handler != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            listener.onLoginSuccessful();
                        }
                    });
                } else {
                    listener.onLoginSuccessful();
                }
            }
        }
    }

    public void login(final LoginInfo loginInfo) {
        if (!hasGroupAuth())
            throw new RuntimeException("No Group auth");//TODO own exception

        communicator.login(groupID, loginInfo, new Callback<UserAuth>() {
            @Override
            public void success(UserAuth userAuth, Response response) {
                setUserAuth(userAuth);
                setUserName(loginInfo.name);
                notifyLoginSuccessful();
            }

            @Override
            public void failure(RetrofitError e) {
                Log.e(THIS, "Login failed", e);
                notifyConnectionFailed(e, e.getResponse().getStatus());
            }
        });
    }

    public RetroAuthCommunicator getAuthCommunicator() throws NoServerKnownException {
        if (!hasUserAuth())
            throw new NoServerKnownException("Trying to access the Auth API of a Server without having Auth data");

        if (authCommunicator == null)
            authCommunicator = serverHandle.getAuthApi();
        return authCommunicator;
    }

    public RetroCommunicator getCommunicator() {
        return communicator;
    }

    public PictureIdResolver getPictureResolver() {
        return new PictureIdResolver(address);
    }

    public void save() throws IOException {
        ObjectMapper mapper = Serialization.getJsonInstance();
        mapper.writeValue(Storage.getServerConfigOutputStream(), this);
    }

    public boolean tryLogout() {
        try {
            getAuthCommunicator().logout(currentServer.groupID, new Callback<Response>() {
                @Override
                public void success(Response response, Response response2) {
                    Log.d(THIS, "Logged out of old Server");
                }

                @Override
                public void failure(RetrofitError e) {
                    Log.e(THIS, "Could not logout old Server", e);
                }
            });
            return true;
        } catch (NoServerKnownException e) {
            Log.e(THIS, "Could not logout old Server", e);
            return false;
        }
    }

    public IChatManager acquireChatManager(Object handle) throws NoServerKnownException {
        if (chatManager == null) {
            chatManager = new ChatManager(getAuthCommunicator(), Storage.getDatabaseProvider());
        }
        return chatManager;
    }

    public void releaseChatManager(Object handle) {

    }

    public ITaskManager acquireTaskManager(Object handle) throws NoServerKnownException {
        if (taskManager == null) {
            taskManager = new TaskManager(getAuthCommunicator(), Storage.getDatabaseProvider());
        }
        return taskManager;
    }

    public void releaseTaskManager(Object handle) {

    }

    public IMapManager acquireMapManager(Object handle) throws NoServerKnownException {
        if (mapManager == null) {
            mapManager = new MapManager(getAuthCommunicator(), Storage.getDatabaseProvider());
        }
        return mapManager;
    }

    public void releaseMapManager(Object handle) {

    }

    public String getAvatarUrl(int groupID) {
        return address + Paths.getAvatar(groupID);
    }

    public GroupUser getUser() {
        return new GroupUser(this.userAuth.userID, this.groupID, this.userName);
    }

    public String getAddress() {
        return address;
    }

    public String getServerIconUrl() {
        return address + Paths.SERVER_PICTURE;
    }

    public List<Group> getAvailableGroups() {
        return groups;
    }

    public String serialize() {
        ObjectMapper mapper = Serialization.getJsonInstance();
        try {
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            Log.e(THIS, "Could not Serialize Server", e);
            return null;
        }
    }

    public String getPictureUploadURL(String hash) {
        return address + Paths.PICS + "/" + hash;
    }

    public ServerLogin exportLogin() {
        return new ServerLogin(address, groupID, groupPassword);
    }

    private void setUserName(String userName) {
        this.userName = userName;
    }

    public void forceRefreshUsers() {
        synchronized (this) {
            if (refreshingUsers) {
                Log.w(THIS, "Preventing concurrent User refreshes");
                return;
            }
            refreshingUsers = true;
        }

        getAuthCommunicator().getAllUsers(new Callback<List<GroupUser>>() {
            @Override
            public void success(List<GroupUser> users, Response response) {
                writeUsers(users);
                if (chatManager != null) {
                    chatManager.onUsersChanged();
                }

                synchronized (this) {
                    refreshingUsers = false;
                }
            }

            @Override
            public void failure(RetrofitError e) {
                //TODO Server.commError()
                Log.e(THIS, "unable to get Groups", e);

                synchronized (this) {
                    refreshingUsers = false;
                }
            }
        });
    }

    private void writeGroups() {
        Log.v(THIS, "Saving Groups:" + groups);

        final SQLiteDatabase db = Storage.getDatabaseProvider().getDatabase();
        db.delete(Groups.TABLE, null, null);

        for (Group g : groups) {
            ContentValues insert = new ContentValues();
            insert.put(Groups.KEY_ID, g.groupID);
            insert.put(Groups.KEY_NAME, g.name);
            insert.put(Groups.KEY_DESCRIPTION, g.description);
            db.insert(Groups.TABLE, null, insert);
        }
    }

    public void writeUsers(List<GroupUser> users) {
        Log.v(THIS, "Saving Users");

        final SQLiteDatabase db = Storage.getDatabaseProvider().getDatabase();

        db.delete(Users.TABLE, null, null);

        for (GroupUser u : users) {
            ContentValues insert = new ContentValues();
            insert.put(Users.KEY_ID, u.userID);
            insert.put(Users.KEY_NAME, u.name);
            insert.put(Users.FOREIGN_GROUP, u.groupID);
            db.insert(Users.TABLE, null, insert);
        }
    }

    public void logout() {
        boolean hasServer = tryLogout();
        if (hasServer) {
            Storage.deleteServerConfig();
            currentServer = null;
            notifyCurrentServerChanged();
        }
    }
}