com.ebay.cloud.cms.metadata.mongo.MetadataOptionValidator.java Source code

Java tutorial

Introduction

Here is the source code for com.ebay.cloud.cms.metadata.mongo.MetadataOptionValidator.java

Source

/*
Copyright [2013-2014] eBay Software Foundation
    
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.
*/

/**
 * 
 */
/* 
Copyright 2012 eBay Software Foundation 
    
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.ebay.cloud.cms.metadata.mongo;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import com.ebay.cloud.cms.consts.CMSConsts;
import com.ebay.cloud.cms.metadata.exception.IllegalIndexException;
import com.ebay.cloud.cms.metadata.exception.IndexExistsException;
import com.ebay.cloud.cms.metadata.exception.IndexNotExistsException;
import com.ebay.cloud.cms.metadata.exception.IndexOptionOperationException;
import com.ebay.cloud.cms.metadata.model.IMetadataVisistor;
import com.ebay.cloud.cms.metadata.model.IndexInfo;
import com.ebay.cloud.cms.metadata.model.MetaClass;
import com.ebay.cloud.cms.metadata.model.MetaClassGraph;
import com.ebay.cloud.cms.metadata.model.MetaField;
import com.ebay.cloud.cms.metadata.model.MetaField.DataTypeEnum;
import com.ebay.cloud.cms.metadata.model.MetaOption;
import com.ebay.cloud.cms.metadata.model.MetaRelationship;
import com.ebay.cloud.cms.metadata.model.MetaRelationship.RelationTypeEnum;
import com.ebay.cloud.cms.metadata.model.Repository;
import com.ebay.cloud.cms.metadata.model.RepositoryOption;
import com.ebay.cloud.cms.metadata.service.IMetadataService;
import com.ebay.cloud.cms.metadata.service.MetadataContext;

/**
 * @author liasu
 * 
 */
public class MetadataOptionValidator implements IMetadataVisistor {

    private final MetadataContext context;
    private final MetaOption newOption;
    private MetaOption tempOption;
    private Set<String> visited;

    public MetadataOptionValidator(MetaOption option, MetadataContext context) {
        this.context = context;
        this.newOption = option;
        this.visited = new HashSet<String>();
    }

    @Override
    public void processField(MetaClass metaClass, MetaField field) {
        // nothing to do
    }

    @Override
    public void processOption(MetaClass metaClass, MetaOption option, MetaClassGraph tempGraph) {
        tempOption = new MetaOption();
        Collection<IndexInfo> newIndexes = newOption.getIndexes();
        for (IndexInfo ii : newIndexes) {
            IndexInfo newIndex = new IndexInfo(ii.getIndexName());
            tempOption.addIndex(newIndex);
        }

        Collection<IndexInfo> oldIndexes = option.getIndexes();
        Map<String, IndexInfo> oldIndexMap = new HashMap<String, IndexInfo>();
        for (IndexInfo oldi : oldIndexes) {
            oldIndexMap.put(oldi.getIndexName(), oldi);
        }

        StringBuilder sb = new StringBuilder();
        switch (context.getOptionChangeMode()) {
        case ADD:
            checkIndexSize(metaClass, newOption, tempGraph);
            for (IndexInfo ii : newIndexes) {
                if (ii.isInternal()) {
                    continue;
                }
                if (oldIndexMap.containsKey(ii.getIndexName())) {
                    sb.append(MessageFormat.format("index {0} already exsits on metaclass {1}!", ii.getIndexName(),
                            metaClass.getName())).append("\n");
                }
                ii.validate();
                validateKeyList(metaClass, ii);
            }
            if (sb.length() > 0) {
                throw new IndexExistsException(sb.toString());
            }

            // check all descendant metaclass
            List<MetaClass> descendants = metaClass.getDescendants();
            for (MetaClass desc : descendants) {
                desc.traverse(this);
            }
            break;
        case UPDATE:
            checkIndexSize(metaClass, newOption, tempGraph);
            for (IndexInfo ii : newIndexes) {
                if (ii.isInternal()) {
                    continue;
                }
                ii.validate();
                validateKeyList(metaClass, ii);
            }
            // pass through
        case DELETE:
            for (IndexInfo ii : newIndexes) {
                if (ii.isInternal()) {
                    continue;
                }

                if (!oldIndexMap.containsKey(ii.getIndexName())) {
                    sb.append(MessageFormat.format("index {0} doesn''t exsits", ii.getIndexName())).append("\n");
                }
            }
            if (sb.length() > 0) {
                throw new IndexNotExistsException(sb.toString());
            }
            break;
        case VALIDATE:
            checkIndexSize(metaClass, newOption, tempGraph);
            for (IndexInfo ii : oldIndexes) {
                if (ii.isInternal()) {
                    continue;
                }
                ii.validate();
            }
            break;
        default:
            throw new IndexOptionOperationException("Illegal update mode for metaclass option change");
        }

    }

    private void validateKeyList(MetaClass meta, IndexInfo ii) {
        for (String key : ii.getKeyList()) {
            if (meta.getFieldByName(key) == null) {
                throw new IndexOptionOperationException(
                        "MetaClass " + meta.getName() + "'sindex " + ii.getIndexName()
                                + " key list contains non-existing field " + key + " in meta " + meta.getName());
            }
        }
    }

    private Collection<IndexInfo> getEmbededIndexes(MetaClass metaClass, MetaClassGraph graph) {
        Collection<IndexInfo> embedIndexes = new ArrayList<IndexInfo>();
        Collection<MetaField> fields = metaClass.getClassFields();
        for (MetaField field : fields) {
            if (field.getDataType() == DataTypeEnum.RELATIONSHIP
                    && ((MetaRelationship) field).getRelationType() == RelationTypeEnum.Embedded) {
                MetaRelationship relationship = (MetaRelationship) field;
                String refDataType = relationship.getRefDataType();
                MetaClass targetMeta = graph.getMetaClass(refDataType);
                if (targetMeta != null) {
                    // add reference indexes
                    targetMeta.addReferenceIndexes();
                    // add self indexes
                    Collection<IndexInfo> targetIndexes = new ArrayList<IndexInfo>(
                            targetMeta.getOptions().getIndexes());
                    // add nested embed indexes
                    targetIndexes.addAll(getEmbededIndexes(targetMeta, graph));
                    for (IndexInfo targetIndex : targetIndexes) {
                        embedIndexes.add(new IndexInfo(targetIndex, relationship));
                    }
                }
            }
        }
        return embedIndexes;
    }

    private void checkIndexSize(MetaClass metaClass, MetaOption newOption, MetaClassGraph tempGraph) {
        // add indexes from reference fields
        metaClass.addReferenceIndexes();

        // add indexes from embed fields
        Collection<IndexInfo> embedIndexes = getEmbededIndexes(metaClass, tempGraph);

        IMetadataService metadataService = metaClass.getMetadataService();
        Repository repo = metadataService.getRepository();
        RepositoryOption repositoryOption = repo.getOptions();
        Integer maxNumOfIndexes = CMSConsts.MAX_INDEXES_PER_META_CLASS;
        if (repositoryOption != null && repositoryOption.getMaxNumOfIndexes() != null) {
            maxNumOfIndexes = repositoryOption.getMaxNumOfIndexes();
        }

        Collection<IndexInfo> existedIndexes = metaClass.getIndexes();
        Collection<IndexInfo> newIndexes = newOption.getIndexes();
        Set<String> mergedIndexNames = new HashSet<String>();
        Set<String> existedIndexNames = new HashSet<String>();
        for (IndexInfo ii : existedIndexes) {
            existedIndexNames.add(ii.getIndexName());
            mergedIndexNames.add(ii.getIndexName());
        }
        for (IndexInfo ii : newIndexes) {
            mergedIndexNames.add(ii.getIndexName());
        }
        for (IndexInfo ii : embedIndexes) {
            mergedIndexNames.add(ii.getIndexName());
        }

        int actualIndexSize = mergedIndexNames.size();
        if (actualIndexSize > maxNumOfIndexes) {
            String errorMessage = String.format(
                    "The number of index on a metaclass %s should NOT be more than %d! Existed index names: %s, while target index names: %s",
                    metaClass.getName(), maxNumOfIndexes, StringUtils.join(existedIndexNames.iterator(), ", "),
                    StringUtils.join(mergedIndexNames.iterator(), ", "));
            throw new IllegalIndexException(errorMessage);
        }

        // if this is embed class, check container index size also
        if (metaClass.isEmbed()) {
            List<MetaRelationship> fromRefs = tempGraph.getFromReference(metaClass);
            for (MetaRelationship fromRef : fromRefs) {
                String containerMetaClassName = fromRef.getSourceDataType();
                if (containerMetaClassName != null) {
                    MetaClass containerMetaClass = tempGraph.getMetaClass(containerMetaClassName);
                    containerMetaClass.setMetadataService(metadataService);

                    Collection<IndexInfo> tempIndexes = tempOption.getIndexes();
                    for (IndexInfo tempIndex : tempIndexes) {
                        IndexInfo newIndex = new IndexInfo(tempIndex, fromRef);
                        tempIndex.setIndexName(newIndex.getIndexName());
                    }
                    checkIndexSize(containerMetaClass, tempOption, tempGraph);
                }
            }
        } else {
            // check descendants violated or not as indexes allow inheritance
            List<MetaClass> descendants = tempGraph.getDescendants(metaClass);
            for (MetaClass descendant : descendants) {
                String metaClassName = descendant.getName();
                if (!visited.contains(metaClassName)) {
                    visited.add(metaClassName);
                    checkIndexSize(descendant, newOption, tempGraph);
                }
            }
        }
    }

    @Override
    public Collection<String> getVisitFields(MetaClass metaClass) {
        return Collections.emptyList();
    }

}