com.rathravane.drumlin.examples.accounts.exampleAcctPersistence.java Source code

Java tutorial

Introduction

Here is the source code for com.rathravane.drumlin.examples.accounts.exampleAcctPersistence.java

Source

/*
 *   Copyright 2006-2013, Rathravane LLC
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *   
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package com.rathravane.drumlin.examples.accounts;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.rathravane.drumlin.app.accounts.DrumlinAccountPersistence;
import com.rathravane.drumlin.app.accounts.DrumlinAccountsException;
import com.rathravane.drumlin.app.accounts.DrumlinApiKey;
import com.rathravane.till.logging.rrLogSetup;
import com.rathravane.till.store.rrJsonObjectFile;

public class exampleAcctPersistence implements DrumlinAccountPersistence<exampleUser, exampleAccount> {
    public static void initialize(File file, String passwd) throws IOException {
        rrJsonObjectFile.initialize(file, 1024);

        final rrJsonObjectFile store = new rrJsonObjectFile(file, true, passwd);

        // the first record in the file is an index container
        final long indexLocation = store.indexToAddress(0);
        final JSONObject index = new JSONObject();
        for (String indexName : new String[] { kIndex_UserList, kIndex_AcctList, kIndex_ApiKeys,
                kIndex_RequestTags }) {
            index.put(indexName, new JSONObject());
        }
        store.overwrite(indexLocation, index);
        store.close();
    }

    public exampleAcctPersistence(rrJsonObjectFile store) throws IOException {
        fStore = store;
        fIndex = store.read(store.indexToAddress(0));
        // the index remains in memory, with changes written back to the file 
    }

    @Override
    public String getAppSignature() {
        return "The app signature is a constant string that introduces app-specific data into tag creation and password hashing.";
    }

    @Override
    public void close() {
        try {
            fStore.close();
        } catch (IOException e) {
            log.warn("Error closing store. " + e.getMessage(), e);
        }
    }

    @Override
    public boolean userExists(String uid) throws DrumlinAccountsException {
        return (loadUser(uid) != null);
    }

    @Override
    public exampleUser createUser(String acctId, String userId, String userEmail) throws DrumlinAccountsException {
        final JSONObject user = new JSONObject();
        user.put(kField_Type, kValue_User);
        user.put(kField_AcctId, acctId);
        user.put(kField_Enabled, false);

        final long loc = writeAndIndex(user, kIndex_UserList, userId);
        return new exampleUser(loc, user);
    }

    @Override
    public void deleteUser(String userId) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null) {
            removeFromIndex(kIndex_UserList, userId);
            try {
                fStore.delete(ri.location);
            } catch (IOException e) {
                throw new DrumlinAccountsException(e);
            }
        }
    }

    @Override
    public exampleUser loadUser(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        if (ri != null) {
            return new exampleUser(ri.location, ri.record);
        } else {
            return null;
        }
    }

    @Override
    public String getAccountIdForUser(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        return ri != null ? ri.record.optString(kField_AcctId, null) : null;
    }

    @Override
    public boolean isUserEnabled(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        return ri != null && ri.record.optBoolean(kField_Enabled, false);
    }

    @Override
    public void enableUser(String id, boolean enabled) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        if (ri != null) {
            ri.record.put(kField_Enabled, enabled);
            ri.write();
        }
    }

    @Override
    public void storePasswordInfo(String id, String salt, String hashedPassword) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        if (ri != null) {
            ri.record.put(kField_Salt, salt);
            ri.record.put(kField_HashedPassword, hashedPassword);
            ri.write();
        }
    }

    @Override
    public String[] getPasswordInfo(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, id, kValue_User);
        if (ri != null) {
            return new String[] { ri.record.optString(kField_Salt, ""),
                    ri.record.optString(kField_HashedPassword, ""), };
        }
        return new String[] { "", "" };
    }

    @Override
    public boolean accountExists(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_AcctList, id, kValue_Acct);
        return ri != null;
    }

    @Override
    public exampleAccount createAccount(String id) throws DrumlinAccountsException {
        final JSONObject acct = new JSONObject();
        acct.put(kField_Type, kValue_Acct);
        acct.put(kField_Enabled, false);

        final long loc = writeAndIndex(acct, kIndex_AcctList, id);
        return new exampleAccount(loc, acct);
    }

    @Override
    public exampleAccount loadAccount(String id) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_AcctList, id, kValue_Acct);
        if (ri != null) {
            return new exampleAccount(ri.location, ri.record);
        } else {
            return null;
        }
    }

    @Override
    public boolean isAccountEnabled(String acctId) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_AcctList, acctId, kValue_Acct);
        return ri != null && ri.record.optBoolean(kField_Enabled, false);
    }

    @Override
    public void enableAccount(String id, boolean enabled) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_AcctList, id, kValue_Acct);
        if (ri != null) {
            ri.record.put(kField_Enabled, enabled);
            ri.write();
        }
    }

    @Override
    public void storeRequestTag(String userId, String tagType, String tag, long expiresAtEpochTime)
            throws DrumlinAccountsException {
        final JSONObject o = new JSONObject();
        o.put(kField_UserId, userId);
        o.put(kField_Tag, tag);
        o.put(kField_Expires, expiresAtEpochTime);
        o.put(kField_Type, tagType);
        writeAndIndex(o, kIndex_RequestTags, tag);

        // now update the user
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        ri.record.put(kField_Tag + "." + tagType, tag);
        ri.write();
    }

    @Override
    public DrumlinAccountPersistence.tagInfo getRequestTagByTag(String requestTag, String tagType)
            throws DrumlinAccountsException {
        try {
            final recordInfo tagRecord = readIndexedObject(kIndex_RequestTags, requestTag, tagType);
            if (tagRecord == null)
                return null; // no such tag

            return new tagInfo(requestTag, tagRecord.record.getString(kField_UserId),
                    tagRecord.record.getLong(kField_Expires));
        } catch (JSONException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    @Override
    public DrumlinAccountPersistence.tagInfo getRequestTagByUser(String userId, String tagType)
            throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        final String tag = ri.record.optString(kField_Tag + "." + tagType, null);
        if (tag == null)
            return null;

        return getRequestTagByTag(tag, tagType);
    }

    @Override
    public void deleteRequestByTag(String tag, String tagType) throws DrumlinAccountsException {
        try {
            final recordInfo tagRecord = readIndexedObject(kIndex_RequestTags, tag, tagType);
            removeFromIndex(kIndex_RequestTags, tag);
            if (tagRecord != null) {
                fStore.delete(tagRecord.location);
            }
        } catch (IOException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    @Override
    public void deleteRequestTagOnUser(String userId, String tag, String tagType) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null && null != ri.record.remove(kField_Tag + "." + tagType)) {
            ri.write();
        }
    }

    @Override
    public DrumlinApiKey loadApiKey(String incomingKey) throws DrumlinAccountsException {
        try {
            final recordInfo ri = readIndexedObject(kIndex_ApiKeys, incomingKey, kValue_ApiKey);
            if (ri != null) {
                final String apiKey = ri.record.getString(kField_ApiKey);
                final String apiSecret = ri.record.getString(kField_ApiSecret);
                final String apiUser = ri.record.optString(kField_UserId, null);
                final String apiAcct = ri.record.optString(kField_AcctId, null);

                return new DrumlinApiKey() {
                    @Override
                    public String getKey() {
                        return apiKey;
                    }

                    @Override
                    public String getSecret() {
                        return apiSecret;
                    }

                    @Override
                    public DrumlinApiKey.type getAccessType() {
                        return (apiUser != null ? DrumlinApiKey.type.USER : DrumlinApiKey.type.ACCOUNT);
                    }

                    @Override
                    public String getAccount() {
                        return apiAcct;
                    }

                    @Override
                    public String getUser() {
                        return apiUser;
                    }
                };
            }
            return null;
        } catch (JSONException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    @Override
    public Collection<DrumlinApiKey> loadApiKeysForUser(String userId) throws DrumlinAccountsException {
        JSONArray apiKeys = null;

        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null) {
            apiKeys = ri.record.optJSONArray(kField_ApiKey);
        }

        final LinkedList<DrumlinApiKey> result = new LinkedList<DrumlinApiKey>();
        if (apiKeys != null) {
            for (int i = 0; i < apiKeys.length(); i++) {
                try {
                    final DrumlinApiKey a = loadApiKey(apiKeys.getString(i));
                    if (a != null) {
                        result.add(a);
                    }
                } catch (JSONException e) {
                    log.warn("Couldn't load string " + i + " from API key list on user " + userId + ".");
                }
            }
        }
        return result;
    }

    @Override
    public void storeApiKey(DrumlinApiKey apiKey) throws DrumlinAccountsException {
        final JSONObject o = new JSONObject();
        o.put(kField_Type, kValue_ApiKey);
        o.put(kField_ApiKey, apiKey.getKey());
        o.put(kField_ApiSecret, apiKey.getSecret());

        final String userId = apiKey.getUser();
        if (userId != null) {
            o.put(kField_UserId, userId);

            final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
            if (ri != null) {
                JSONArray apiKeys = ri.record.optJSONArray(kField_ApiKey);
                if (apiKeys == null) {
                    apiKeys = new JSONArray();
                    ri.record.put(kField_ApiKey, apiKeys);
                }
                apiKeys.put(apiKey.getKey());
                ri.write();
            } else {
                log.warn("While writing API key " + apiKey.getKey() + ", no such user " + userId + ".");
            }
        }

        final String a = apiKey.getAccount();
        if (a != null) {
            o.put(kField_AcctId, a);
        }

        writeAndIndex(o, kIndex_ApiKeys, apiKey.getKey());
    }

    @Override
    public void deleteApiKey(DrumlinApiKey apiKey) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_ApiKeys, apiKey.getKey(), kValue_ApiKey);
        if (ri != null) {
            try {
                fStore.delete(ri.location);
            } catch (IOException e) {
                throw new DrumlinAccountsException(e);
            }
        }
    }

    @Override
    public void setApiSecret(final String apiKey, final String apiSecret) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_ApiKeys, apiKey, kValue_ApiKey);
        if (ri != null) {
            ri.record.put(kField_ApiSecret, apiSecret);
            ri.write();
        }
    }

    @Override
    public void setUserValue(String userId, String key, String value) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null) {
            ri.record.put(key, value);
            ri.write();
        }
    }

    @Override
    public String getUserValue(String userId, String key) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null)
            return ri.record.optString(kField_Enabled, null);
        return null;
    }

    @Override
    public void removeUservalue(String userId, String key) throws DrumlinAccountsException {
        final recordInfo ri = readIndexedObject(kIndex_UserList, userId, kValue_User);
        if (ri != null)
            ri.record.remove(kField_Enabled);
    }

    private final rrJsonObjectFile fStore;
    private final JSONObject fIndex;

    private static final String kField_Type = "type";
    private static final String kValue_User = "user";
    private static final String kValue_Acct = "account";
    private static final String kValue_ApiKey = "apiKey";

    private static final String kField_Enabled = "enabled";
    private static final String kField_UserId = "userId";
    private static final String kField_Tag = "tag";
    private static final String kField_AcctId = "acctId";
    private static final String kField_Expires = "expires";
    private static final String kField_Salt = "salt";
    private static final String kField_HashedPassword = "hashedPassword";
    private static final String kField_ApiKey = "apiKey";
    private static final String kField_ApiSecret = "apiSecret";

    private static final String kIndex_ApiKeys = "apiKeys";
    private static final String kIndex_UserList = "userList";
    private static final String kIndex_AcctList = "acctList";
    private static final String kIndex_RequestTags = "resets";

    private static final org.slf4j.Logger log = rrLogSetup.getLog(exampleAcctPersistence.class);

    private long writeAndIndex(JSONObject o, String indexName, String keyName) throws DrumlinAccountsException {
        try {
            final long loc = fStore.write(o);
            putInIndex(indexName, keyName, loc);
            return loc;
        } catch (IOException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    private void putInIndex(String indexName, String keyName, long recordLoc) throws DrumlinAccountsException {
        try {
            final JSONObject o = fIndex.getJSONObject(indexName);
            o.put(keyName, recordLoc);
            fStore.overwrite(fStore.indexToAddress(0), fIndex);
        } catch (JSONException e) {
            throw new DrumlinAccountsException(e);
        } catch (IOException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    private void removeFromIndex(String indexName, String keyName) throws DrumlinAccountsException {
        try {
            final JSONObject o = fIndex.getJSONObject(indexName);
            o.remove(keyName);
            fStore.overwrite(fStore.indexToAddress(0), fIndex);
        } catch (JSONException e) {
            throw new DrumlinAccountsException(e);
        } catch (IOException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    private class recordInfo {
        public recordInfo(long loc, JSONObject rec) {
            location = loc;
            record = rec;
        }

        public void write() throws DrumlinAccountsException {
            try {
                fStore.overwrite(location, record);
            } catch (IOException e) {
                throw new DrumlinAccountsException(e);
            }
        }

        public final long location;
        public final JSONObject record;
    }

    private recordInfo readIndexedObject(String indexName, String key, String type)
            throws DrumlinAccountsException {
        try {
            final long index = readIndex(indexName, key);
            if (index >= 0) {
                final JSONObject result = fStore.read(index);
                if (result.optString(kField_Type, "").equals(type)) {
                    return new recordInfo(index, result);
                }
            }
            return null;
        } catch (IOException e) {
            throw new DrumlinAccountsException(e);
        }
    }

    private long readIndex(String indexName, String keyName) throws DrumlinAccountsException {
        try {
            final JSONObject o = fIndex.getJSONObject(indexName);
            return o.optLong(keyName, -1);
        } catch (JSONException e) {
            throw new DrumlinAccountsException(e);
        }
    }
}