com.ikanow.infinit.e.data_model.store.config.source.SourcePojoSubstitutionDbMap.java Source code

Java tutorial

Introduction

Here is the source code for com.ikanow.infinit.e.data_model.store.config.source.SourcePojoSubstitutionDbMap.java

Source

/*******************************************************************************
 * Copyright 2012 The Infinit.e Open Source Project
 * 
 * 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.ikanow.infinit.e.data_model.store.config.source;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.bson.BSONObject;
import org.bson.types.ObjectId;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.ikanow.infinit.e.data_model.Globals;
import com.ikanow.infinit.e.data_model.store.BaseDbPojo;
import com.ikanow.infinit.e.data_model.store.BasePojoDbMap;
import com.ikanow.infinit.e.data_model.store.DbManager;
import com.ikanow.infinit.e.data_model.store.MongoDbManager;
import com.ikanow.infinit.e.data_model.store.MongoDbUtil;
import com.ikanow.infinit.e.data_model.store.social.person.PersonCommunityPojo;
import com.ikanow.infinit.e.data_model.store.social.person.PersonPojo;
import com.ikanow.infinit.e.data_model.store.social.sharing.SharePojo;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;

public class SourcePojoSubstitutionDbMap implements BasePojoDbMap<SourcePojo> {

    protected ObjectId _callingUserId;
    protected SourcePojoSubstitutionDeserializer _errorHandler1;
    protected SourcePojoDeserializer _errorHandler2;

    // NOTE: this is not currently needed because the only time one user can call another's source is from the API test,
    // and that case is handled by the SourcePojoApiSubstitutionMap (note: which doesn't have to worry about SourcePojo deserialization
    // since the extractor options are in "." notation, not converted - it handles the federated query pojo by hand)
    public SourcePojoSubstitutionDbMap(ObjectId callingUserId) {
        _callingUserId = callingUserId;
    }

    public SourcePojoSubstitutionDbMap() {
        // No calling id known at this point, so we'll do it in 2 steps:
        // 1) grab the ownerId from the JSON
        // 2) then apply the per-string deser
    }

    @Override
    public GsonBuilder extendBuilder(GsonBuilder gp) {
        if (null != _callingUserId) {
            return new SourcePojo().extendBuilder(gp.registerTypeAdapter(String.class,
                    (_errorHandler1 = new SourcePojoSubstitutionDeserializer(_callingUserId))));
        } else {
            return gp.registerTypeAdapter(SourcePojo.class, (_errorHandler2 = new SourcePojoDeserializer()));
        }
    }

    public List<String> getErrorMessages() {
        if (null != _errorHandler1) {
            return _errorHandler1.getErrMessages();
        } else if (null != _errorHandler2) {
            return _errorHandler2.getErrMessages();
        } else {
            return null;
        }
    }

    protected static class SourcePojoDeserializer implements JsonDeserializer<SourcePojo> {
        protected SourcePojoSubstitutionDeserializer _errorHandler1;

        public List<String> getErrMessages() {
            if (null != _errorHandler1) {
                return _errorHandler1.getErrMessages();
            } else {
                return null;
            }
        }

        @Override
        public SourcePojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            JsonElement ownerIdJson = json.getAsJsonObject().get(SourcePojo.ownerId_);
            if (null != ownerIdJson) {
                String ownerIdStr = null;
                ObjectId ownerId = null;
                try {
                    ownerIdStr = ownerIdJson.getAsString();
                    ownerId = new ObjectId(ownerIdStr);
                } catch (Exception e) { // this is fine, just means it's an $oid
                    try {
                        ownerIdStr = ownerIdJson.getAsJsonObject().get("$oid").getAsString();
                        ownerId = new ObjectId(ownerIdStr);
                    } catch (Exception ee) {
                    } // just carry on - this isn't a source sub specific error, and it will break elsewhere
                }
                if (null != ownerId) {
                    // (the following horror is necessary because the SourcePojo.extendBuilder re-creates its own chain hence ignores the string adapter registered here)
                    // Will apply source pojo deser, with extraction options handling
                    GsonBuilder gp1 = new SourcePojo().extendBuilder(BaseDbPojo.getDefaultBuilder())
                            .registerTypeAdapter(String.class,
                                    (_errorHandler1 = new SourcePojoSubstitutionDeserializer(ownerId)));
                    // Will apply the string manipulation but no the extractor logic 
                    GsonBuilder gp2 = BaseDbPojo.getDefaultBuilder().registerTypeAdapter(String.class,
                            (_errorHandler1 = new SourcePojoSubstitutionDeserializer(ownerId)));

                    SourcePojo intermediate1 = gp1.create().fromJson(json, SourcePojo.class); // full deser - source but no subs
                    Gson really = gp2.create();
                    JsonElement intermediate2 = really.toJsonTree(intermediate1); // partial ser - only string options
                    return really.fromJson(intermediate2, SourcePojo.class);
                }
            }
            // Fallback to default serialization
            return new SourcePojo().extendBuilder(SourcePojo.getDefaultBuilder()).create().fromJson(json,
                    SourcePojo.class);
        }
    }//TESTED

    public static class SourcePojoSubstitutionDeserializer implements JsonDeserializer<String> {
        protected ObjectId _callingUserId;
        protected static Pattern SUBVARIABLE = Pattern.compile("#IKANOW\\{([^.}]+)[.]([^}]+)\\}",
                Pattern.CASE_INSENSITIVE);
        // Options inside the #IKANOW{}:
        // <source_type>.var where source_type is any JSON field meeting criteria source_type && owner by _callingUserId
        // <sourceid>.var

        // cache
        protected HashMap<String, BasicDBObject> _savedCredentials = null;
        protected List<ObjectId> _userCommunities = null;

        public SourcePojoSubstitutionDeserializer(ObjectId callingUserId) {
            _callingUserId = callingUserId;
        }

        @Override
        public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {

            String val = json.getAsString();
            Matcher m = SUBVARIABLE.matcher(val);
            StringBuffer sb = null;
            while (m.find()) {
                if (null == sb) {
                    sb = new StringBuffer();
                }
                String replaceStr = m.group(); // (will be overwritten if the sub succeeds)
                String sourceInfo = m.group(1); // source type (must be owner) or source id
                String fieldPath = m.group(2);

                BasicDBObject credentials = null;
                String authCredential = null;
                if (null == _savedCredentials) {
                    _savedCredentials = new HashMap<String, BasicDBObject>();
                } else {
                    credentials = _savedCredentials.get(sourceInfo);
                } //TESTED
                if (null == credentials) {
                    credentials = populateCredentials(sourceInfo);
                    if (null == credentials) { // permissison error, already logged issue 
                        continue;
                    }
                } //TESTED
                authCredential = MongoDbUtil.getProperty(credentials, fieldPath);

                if (null != authCredential) {
                    replaceStr = authCredential;
                } else {
                    addErrMessage("Couldn't find credential: " + fieldPath);
                }
                m.appendReplacement(sb, replaceStr);
            }
            if (null != sb) {
                m.appendTail(sb);
                val = sb.toString();
            }
            return val;
        }

        ///////////////////////

        // UTILS

        protected BasicDBObject populateCredentials(String sourceInfo) {
            BasicDBObject retVal = null;
            try {
                ObjectId sourceId = new ObjectId(sourceInfo);
                try {
                    // If here then grab source if I'm allowed
                    // a) get my communities:
                    if (null == _userCommunities) {
                        BasicDBObject personQuery = new BasicDBObject("_id", _callingUserId);
                        PersonPojo person = PersonPojo.fromDb(
                                MongoDbManager.getSocial().getPerson().findOne(personQuery), PersonPojo.class);
                        if (null == person) {
                            addErrMessage("Didn't find user: " + _callingUserId);
                            return null;
                        }
                        _userCommunities = new ArrayList<ObjectId>(person.getCommunities().size());
                        for (PersonCommunityPojo personComm : person.getCommunities()) {
                            _userCommunities.add(personComm.get_id());
                        }
                    } //TESTED (by hand)

                    // b) try getting the source
                    BasicDBObject shareQuery = new BasicDBObject(SharePojo._id_, sourceId);
                    shareQuery.put(SharePojo.ShareCommunityPojo.shareQuery_id_,
                            new BasicDBObject(DbManager.in_, _userCommunities));
                    SharePojo share = SharePojo.fromDb(MongoDbManager.getSocial().getShare().findOne(shareQuery),
                            SharePojo.class);
                    if (null == share) {
                        addErrMessage("Couldn't find share, or don't have read access: " + sourceInfo);
                        return null;
                    }
                    _savedCredentials.put(sourceInfo,
                            (retVal = (BasicDBObject) com.mongodb.util.JSON.parse(share.getShare())));
                } catch (Exception e) {
                    StringBuffer sbErr = new StringBuffer();
                    Globals.populateStackTrace(sbErr, e);
                    addErrMessage("Unknown error: " + sbErr.toString());
                    return null; // (do nothing)
                }
            } //TESTED (including cache, by hand)
            catch (Exception e) {
                // Source info is a share type
                BasicDBObject shareQuery = new BasicDBObject(SharePojo.type_, sourceInfo);
                shareQuery.put(SharePojo.ShareOwnerPojo.shareQuery_id_, _callingUserId);

                List<SharePojo> credentialShares = SharePojo
                        .listFromDb(DbManager.getSocial().getShare().find(shareQuery), SharePojo.listType());
                if ((null == credentialShares) || credentialShares.isEmpty()) {
                    addErrMessage("Couldn't find shares of this type, or don't have read access: " + sourceInfo);
                    return null;
                } //TESTED (by hand)
                retVal = new BasicDBObject();
                for (SharePojo share : credentialShares) {
                    try {
                        Object x = com.mongodb.util.JSON.parse(share.getShare());
                        if (x instanceof BasicDBList) { // It's a list add all the k/vs from all the array els
                            BasicDBList xl = (BasicDBList) x;
                            for (Object o : xl) {
                                retVal.putAll((BSONObject) o);
                            }
                        } else { // (normal case, it's an object just add all k/vs)
                            retVal.putAll((BSONObject) x);
                        }
                    } catch (Exception ee) {
                        StringBuffer sbErr = new StringBuffer();
                        Globals.populateStackTrace(sbErr, e);
                        addErrMessage("Failed to parse: " + share.get_id() + " : " + share.getTitle() + ": "
                                + sbErr.toString());
                    }
                } //TESTED (by hand)
                if (retVal.isEmpty()) {
                    addErrMessage("Failed to add any fields from: " + sourceInfo);
                    return null;
                }
                _savedCredentials.put(sourceInfo, retVal);
            } //TESTED

            return retVal;
        }//TESTED

        ////////////////////////////

        // Error Handling

        protected List<String> _errMessages;

        public void addErrMessage(String errMessage) {
            if (null == _errMessages) {
                _errMessages = new LinkedList<String>();
            }
            _errMessages.add(errMessage);
        }

        public List<String> getErrMessages() {
            return _errMessages;
        }

    }
}