/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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.queplix.core.utils.www;

import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.cache.Cache;
import com.queplix.core.utils.cache.CacheFactory;

import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Cache manager servlet.
 * The following actions are available:
 * <p/>
 * Get the list of all instantiated caches.
 * <br>
 * Usage: <pre>/cacheManager/list</pre>
 * </p>
 * <p/>
 * <p/>
 * Get cache keys.
 * <br>
 * Usage: <pre>/cacheManager/keys?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>name</b> - the cache name</li>
 * </ul>
 * </p>
 * <p/>
 * <p/>
 * Get cache info.
 * <br>
 * Usage: <pre>/cacheManager/info?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>name</b> - the cache name</li>
 * <li><b>pos</b> - the cache key position</li>
 * </ul>
 * </p>
 * <p/>
 * <p/>
 * Reset cache.
 * <br>
 * Usage: <pre>/cacheManager/clear?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>name</b> - the cache name</li>
 * </ul>
 * </p>
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.3 $ $Date: 2005/10/26 14:03:51 $
 */

public class CacheManagerServlet
        extends AbstractServlet implements SingleThreadModel {

    // ===================================================== Constants

    public static final String LIST_NAME = "list_name";
    public static final String KEYS_NAME = "keys_name";

    // ===================================================== Servlet API methods

    /* (non-Javadoc)
     * @see HttpServlet#service(HttpServletRequest, HttpServletResponse)
     */

    public void service(HttpServletRequest request,
                        HttpServletResponse response)
            throws IOException, ServletException {

        // Check permissions.
        //if( SystemHelper.isProductionMode() ) {
        //    throw new ServletException( "Cannot use this servlet in production mode" );
        //}

        // Get action.
        String action = request.getPathInfo();
        if(action != null) {
            action = action.substring(1);
        } else {
            action = StringHelper.EMPTY_VALUE;
        }

        // Action switch.
        try {
            if(action.equalsIgnoreCase("list")) {
                doListAction(request, response);
            } else if(action.equalsIgnoreCase("keys")) {
                doKeysAction(request, response);
            } else if(action.equalsIgnoreCase("info")) {
                doInfoAction(request, response);
            } else if(action.equalsIgnoreCase("clear")) {
                doClearAction(request, response);
            } else {
                throw new IllegalStateException("Unknown action: " + action);
            }
        } catch (SecurityException ex) {
            throw new ServletException(ex);
        }
    }

    // ========================================================== Action handlers

    // List of Cache objects.

    private void doListAction(HttpServletRequest request,
                              HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        List ret = new ArrayList();
        CacheFactory cf = CacheFactory.getInstance();
        Map cacheMap = cf.getMap();

        for(Iterator it = cacheMap.keySet().iterator(); it.hasNext();) {
            String name = (String) it.next();
            Cache cache = (Cache) cacheMap.get(name);
            int size = -1;
            try {
                size = cache.keys().size();
            } catch (Exception ex) {
            }

            CacheVO vo = new CacheVO(name, size, getSizeInBytes(cache));
            ret.add(vo);
        }

        // put in request
        request.setAttribute(LIST_NAME, ret);

        // make redirect
        request.getRequestDispatcher("/tools/cache/showCacheList.jsp").forward(
                request, response);
    }

    // Get cache keys.
    private void doKeysAction(HttpServletRequest request,
                              HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        String name = ServletHelper.getParamAsString(request, "name");
        request.setAttribute("name", name);

        // Get

        CacheFactory cf = CacheFactory.getInstance();
        Cache cache = cf.getCache(name);
        if(cache == null) {
            throw new NullPointerException();
        }

        Collection keys = null;
        try {
            keys = cache.keys();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        CacheKeysVO ck = new CacheKeysVO(keys);

        // store in the session
        request.getSession().setAttribute(KEYS_NAME, ck);

        // put in request
        request.setAttribute(KEYS_NAME, ck);

        // make redirect
        request.getRequestDispatcher("/tools/cache/showCacheKeys.jsp").forward(
                request, response);
    }

    // Get cache info.
    private void doInfoAction(HttpServletRequest request,
                              HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        String name = ServletHelper.getParamAsString(request, "name");
        int pos = ServletHelper.getParamAsInt(request, "pos");

        // Get CacheInfoVO from the session
        CacheKeysVO ck = (CacheKeysVO) request.getSession().getAttribute(
                KEYS_NAME);
        if(ck == null) {
            throw new NullPointerException(KEYS_NAME + " parameter not found");
        }

        Object key = ck.getKeys().get(pos);
        CacheFactory cf = CacheFactory.getInstance();
        Cache cache = cf.getCache(name);
        if(cache == null) {
            throw new NullPointerException("Cache '" + name + "' not found");
        }

        Object data = cache.get(key);

        // Print Out.
        response.getWriter().print(data == null ? "<EMPTY>":"" + data);
    }

    // Clear cache.
    private void doClearAction(HttpServletRequest request,
                               HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        String name = ServletHelper.getParamAsString(request, "name");
        request.setAttribute("name", name);

        CacheFactory cf = CacheFactory.getInstance();
        Cache cache = cf.getCache(name);
        if(cache == null) {
            throw new NullPointerException();
        }

        cache.clear();

        // make redirect
        response.sendRedirect("/tools/cache/index.jsp");
    }

    // ========================================================= Private methods

    // Returns size of Cache in bytes.

    private int getSizeInBytes(Cache cache) {

        int ret = -1;
        ObjectOutputStream oos = null;

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(cache.toMap());
            ret = baos.size();

        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                oos.close();
            } catch (Exception _e) {
            }
        }

        return ret;
    }

    // ========================================================= Inner classes

    /**
     * <p>Cache VO Class</p>
     *
     * @author [ALB] Baranov Andrey
     * @version 1.0
     */
    public static class CacheVO {

        public String name;
        public int size;
        public int sizeInBytes;

        CacheVO(String name, int size, int sizeInBytes) {
            this.name = name;
            this.size = size;
            this.sizeInBytes = sizeInBytes;
        }
    }


    /**
     * <p>Cache Keys VO Class</p>
     *
     * @author [ALB] Baranov Andrey
     * @version 1.0
     */
    public static class CacheKeysVO {
        public List keys;

        CacheKeysVO(Collection _keys) {

            if(_keys != null) {
                if(!(_keys instanceof List)) {
                    keys = new ArrayList(_keys);
                } else {
                    this.keys = (List) _keys;
                }
            }
        }

        public List getKeys() {
            return keys;
        }

    }
}
