/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.cache;

import java.util.*;

import jp.ossc.nimbus.core.*;

/**
 * キャッシュサービス抽象クラス。<p>
 *
 * @author M.Takata
 */
public abstract class AbstractCacheService<E> extends ServiceBase
 implements CacheRemoveListener<E>, AbstractCacheServiceMBean<E>{
    
    private static final long serialVersionUID = 4327482025283418963L;
    
    /**
     * キャッシュ参照の集合。<p>
     */
    protected Set<CachedReference<E>> references;
    
    /**
     * OverflowControllerサービスのサービス名配列。<p>
     */
    protected ServiceName[] overflowControllerServiceNames;
    
    /**
     * OverflowControllerサービスのリスト。<p>
     */
    protected List<OverflowController<E>> overflowControllers;
    
    /**
     * サービス停止時に、キャッシュをクリアするかどうかのフラグ。<p>
     * デフォルトは、false。<br>
     */
    protected boolean isClearOnStop;
    
    /**
     * サービス破棄時に、キャッシュをクリアするかどうかのフラグ。<p>
     * デフォルトは、true。<br>
     */
    protected boolean isClearOnDestroy = true;
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public void setOverflowControllerServiceNames(ServiceName[] names){
        overflowControllerServiceNames = names;
    }
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public ServiceName[] getOverflowControllerServiceNames(){
        return overflowControllerServiceNames;
    }
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public void setClearOnStop(boolean isClear){
        isClearOnStop = isClear;
    }
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public boolean isClearOnStop(){
        return isClearOnStop;
    }
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public void setClearOnDestroy(boolean isClear){
        isClearOnDestroy = isClear;
    }
    
    // AbstractCacheServiceMBeanのJavaDoc
    @Override
    public boolean isClearOnDestroy(){
        return isClearOnDestroy;
    }
    
    /**
     * OverflowControllerを設定する。
     */
    public void setOverflowControllers(List<OverflowController<E>> overflowControllers) {
		this.overflowControllers = overflowControllers;
	}

	/**
     * サービスの生成前処理を行う。<p>
     * インスタンス変数の初期化を行う。<br>
     *
     * @exception 生成前処理に失敗した場合
     */
    @Override
    public void preCreateService() throws Exception{
        super.preCreateService();
        references = Collections.synchronizedSet(new HashSet<CachedReference<E>>());
        overflowControllers = new ArrayList<OverflowController<E>>();
    }
    
    /**
     * サービスの開始前処理を行う。<p>
     * QverflowControllerサービスの取得を行う。<br>
     *
     * @exception 開始前処理に失敗した場合
     */
    @Override
    public void preStartService() throws Exception{
        super.preStartService();
        if(overflowControllerServiceNames != null){
            for(int i = 0; i < overflowControllerServiceNames.length; i++){
                overflowControllers.add(
                    ServiceManagerFactory
                        .<OverflowController<E>>getServiceObject(overflowControllerServiceNames[i])
                );
            }
        }
    }
    
    /**
     * サービスの停止後処理を行う。<p>
     * QverflowControllerサービスの参照を破棄する。<br>
     *
     * @exception 停止後処理に失敗した場合
     */
    @Override
    public void postStopService() throws Exception{
        if(isClearOnStop()){
            clear();
        }
        if(overflowControllers != null){
            overflowControllers.clear();
        }
        super.postStopService();
    }
    
    /**
     * サービスの破棄後処理を行う。<p>
     * インスタンス変数の参照を破棄する。<br>
     *
     * @exception 破棄後処理に失敗した場合
     */
    @Override
    public void postDestroyService() throws Exception{
        if(isClearOnDestroy()){
            clear();
        }
        references = null;
        overflowControllers = null;
        super.postDestroyService();
    }
    
    // CacheのJavaDoc
    @Override
    public CachedReference<E> add(E obj){
        final CachedReference<E> ref = createCachedReference(obj);
        add(ref);
        return ref;
    }
    
    /**
     * 指定されたキャッシュ参照を追加する。<p>
     *
     * @param ref キャッシュ参照
     */
    protected void add(CachedReference<E> ref){
        if(ref == null || references == null || getState().compareTo(State.STOPPED) > 0){
            return;
        }
        synchronized(references){
            ref.addCacheRemoveListener(this);
            references.add(ref);
            if(overflowControllers.size() != 0){
                for(OverflowController<E> controller : overflowControllers){
                    controller.control(ref);
                }
            }
        }
        return;
    }
    
    /**
     * 指定されたオブジェクトのキャッシュ参照を生成する。<p>
     *
     * @param obj キャッシュするオブジェクト
     * @return キャッシュ参照
     */
    protected abstract CachedReference<E> createCachedReference(E obj);
    
    // CacheのJavaDoc
    @Override
    public Iterator<CachedReference<E>> iterator(){
        return new CachedReferenceIterator();
    }
    
    // CacheのJavaDoc
    @Override
    public boolean contains(Object ref){
        if(references == null){
            return false;
        }
        return references.contains(ref);
    }
    
    // CacheのJavaDoc
    @Override
    public boolean containsAll(Collection<?> c){
        if(references == null){
            return false;
        }
        return references.containsAll(c);
    }
    
    // CacheのJavaDoc
    @Override
    public boolean isEmpty(){
        if(references == null){
            return true;
        }
        return references.isEmpty();
    }
    
    // CacheのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public boolean remove(Object ref){
        boolean result = false;
        if(references != null){
            synchronized(references){
                result = references.remove(ref);
                if(result){
                    ((CachedReference<E>)ref).remove(this);
                }
            }
        }
        return result;
    }
    
    // CacheのJavaDoc
    @Override
    public boolean removeAll(Collection<?> c){
        boolean result = false;
        for(Object obj : c){
            result |= remove(obj);
        }
        return result;
    }
    
    // CacheのJavaDoc
    @Override
    public boolean retainAll(Collection<?> c){
        if(references == null){
            return false;
        }
        boolean result = false;
        synchronized(references){
            for(CachedReference<E> ref : references){
                if(!c.contains(ref)){
                    result |= remove(ref);
                }
            }
        }
        return result;
    }
    
    // CacheのJavaDoc
    @Override
    public int size(){
        if(references == null){
            return 0;
        }else{
            return references.size();
        }
    }
    
    // CacheのJavaDoc
    @Override
    public void clear(){
        if(references != null){
            synchronized(references){
                final Object[] refs = references.toArray();
                for(int i = 0; i < refs.length; i++){
                    remove(refs[i]);
                }
            }
        }
    }
    
    // CacheのJavaDoc
    @Override
    public Object[] toArray(){
        if(references == null){
            return new CachedReference[0];
        }
        return references.toArray(
            new CachedReference[references.size()]
        );
    }
    
    // CacheのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public <T> CachedReference<T>[] toArray(CachedReference<T>[] refs){
        if(references == null){
            return new CachedReference[0];
        }
        return references.toArray(refs);
    }
    
    // CacheRemoveListenerのJavaDoc
    @Override
    public void removed(CachedReference<E> ref){
        if(references != null && references.contains(ref)){
            references.remove(ref);
        }
    }
    
    /**
     * キャッシュのキャッシュ参照繰り返し。<p>
     *
     * @author M.Takata
     * @see AbstractCacheService#iterator()
     */
    private class CachedReferenceIterator
     implements Iterator<CachedReference<E>>, java.io.Serializable{
        
        private static final long serialVersionUID = -6125109804226184416L;
        
        private Iterator<CachedReference<E>> iterator;
        private CachedReference<E> current;
        
        /**
         * インスタンスを生成する。<p>
         */
        public CachedReferenceIterator(){
            if(references != null){
                iterator = new HashSet<CachedReference<E>>(references).iterator();
            }
        }
        
        // IteratorのJavaDoc
        @Override
        public boolean hasNext(){
            return iterator == null ? false : iterator.hasNext();
        }
        
        // IteratorのJavaDoc
        @Override
        public CachedReference<E> next(){
            if(iterator == null){
                throw new NoSuchElementException();
            }
            current = null;
            current = iterator.next();
            return current;
        }
        
        // IteratorのJavaDoc
        @Override
        public void remove(){
            if(current == null){
                throw new IllegalStateException();
            }
            iterator.remove();
            AbstractCacheService.this.remove(current);
            if(!iterator.hasNext()){
                current = null;
            }
        }
    }
}
