001package org.apache.commons.jcs3.auxiliary.remote.server;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.io.OutputStream;
024import java.net.InetAddress;
025import java.net.UnknownHostException;
026import java.nio.charset.StandardCharsets;
027import java.util.Properties;
028
029import javax.servlet.ServletConfig;
030import javax.servlet.ServletException;
031import javax.servlet.http.HttpServlet;
032import javax.servlet.http.HttpServletRequest;
033import javax.servlet.http.HttpServletResponse;
034
035import org.apache.commons.jcs3.access.exception.CacheException;
036import org.apache.commons.jcs3.auxiliary.remote.RemoteUtils;
037import org.apache.commons.jcs3.engine.control.CompositeCacheManager;
038import org.apache.commons.jcs3.log.Log;
039import org.apache.commons.jcs3.log.LogManager;
040import org.apache.commons.jcs3.utils.net.HostNameUtil;
041
042/**
043 * This servlet can be used to startup the JCS remote cache. It is easy to
044 * deploy the remote server in a tomcat base. This give you an easy way to
045 * monitor its activity.
046 * <p>
047 * <code>
048 *  servlet&gt;
049        &lt;servlet-name&gt;JCSRemoteCacheStartupServlet&lt;/servlet-name&gt;
050        &lt;servlet-class&gt;
051             org.apache.commons.jcs3.auxiliary.remote.server.RemoteCacheStartupServlet
052        &lt;/servlet-class&gt;
053        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
054    &lt;/servlet&gt;
055
056
057    &lt;servlet-mapping&gt;
058        &lt;servlet-name&gt;JCSRemoteCacheStartupServlet&lt;/servlet-name&gt;
059        &lt;url-pattern&gt;/jcs&lt;/url-pattern&gt;
060    &lt;/servlet-mapping&gt;
061 * </code>
062 */
063public class RemoteCacheStartupServlet
064        extends HttpServlet
065{
066    /** Don't change */
067    private static final long serialVersionUID = 1L;
068
069    /** The logger */
070    private static final Log log = LogManager.getLog(RemoteCacheStartupServlet.class);
071
072    /** The default port to start the registry on. */
073    private static final int DEFAULT_REGISTRY_PORT = 1101;
074
075    /** properties file name */
076    private static final String DEFAULT_PROPS_FILE_NAME = "/cache.ccf";
077
078    /** properties file name, must set prior to calling get instance */
079    private String propsFileName = DEFAULT_PROPS_FILE_NAME;
080
081    /** Configuration properties */
082    private int registryPort = DEFAULT_REGISTRY_PORT;
083
084    /** Configuration properties */
085    private String registryHost;
086
087    /**
088     * Starts the registry and then tries to bind to it.
089     * <p>
090     * Gets the port from a props file. Uses the local host name for the
091     * registry host. Tries to start the registry, ignoring failure. Starts the
092     * server.
093     * <p>
094     *
095     * @throws ServletException
096     */
097    @Override
098    public void init()
099            throws ServletException
100    {
101        super.init();
102
103        loadInitParams();
104        final Properties props = loadPropertiesFromFile();
105
106        if (registryHost == null)
107        {
108            // we will always use the local machine for the registry
109            try
110            {
111                registryHost = HostNameUtil.getLocalHostAddress();
112            }
113            catch (final UnknownHostException e)
114            {
115                log.error("Could not get local address to use for the registry!", e);
116            }
117        }
118
119        log.debug("registryHost = [{0}]", registryHost);
120
121        try
122        {
123            if (InetAddress.getByName(registryHost).isLoopbackAddress())
124            {
125                log.warn("The local address [{0}] is a loopback address. Other machines must "
126                        + "be able to use the address to reach this server.", registryHost);
127            }
128        }
129        catch (final UnknownHostException e)
130        {
131            throw new ServletException("Could not resolve registry host " + registryHost, e);
132        }
133
134        try
135        {
136            if (props == null)
137            {
138                throw new ServletException("Could not load configuration from " + propsFileName);
139            }
140
141            RemoteCacheServerFactory.startup(registryHost, registryPort, props);
142            log.info("Remote JCS Server started with properties from {0}", propsFileName);
143        }
144        catch (final IOException e)
145        {
146            throw new ServletException("Problem starting remote cache server.", e);
147        }
148    }
149
150    /**
151     * It just dumps the stats.
152     * <p>
153     *
154     * @param request
155     * @param response
156     * @throws ServletException
157     * @throws IOException
158     */
159    @Override
160    protected void service(final HttpServletRequest request, final HttpServletResponse response)
161            throws ServletException, IOException
162    {
163        String stats = "";
164
165        try
166        {
167            stats = CompositeCacheManager.getInstance().getStats();
168        }
169        catch (final CacheException e)
170        {
171            throw new ServletException(e);
172        }
173
174        log.info(stats);
175
176        try
177        {
178            String characterEncoding = response.getCharacterEncoding();
179            if (characterEncoding == null)
180            {
181                characterEncoding = StandardCharsets.UTF_8.name();
182                response.setCharacterEncoding(characterEncoding);
183            }
184            final OutputStream os = response.getOutputStream();
185            os.write(stats.getBytes(characterEncoding));
186            os.close();
187        }
188        catch (final IOException e)
189        {
190            log.error("Problem writing response.", e);
191        }
192    }
193
194    /**
195     * shuts the cache down.
196     */
197    @Override
198    public void destroy()
199    {
200        super.destroy();
201
202        log.info("Shutting down remote cache ");
203
204        try
205        {
206            RemoteCacheServerFactory.shutdownImpl(registryHost, registryPort);
207        }
208        catch (final IOException e)
209        {
210            log.error("Problem shutting down.", e);
211        }
212
213        try
214        {
215            CompositeCacheManager.getInstance().shutDown();
216        }
217        catch (final CacheException e)
218        {
219            log.error("Could not retrieve cache manager instance", e);
220        }
221    }
222
223    /**
224     * Load configuration values from config file if possible
225     */
226    private Properties loadPropertiesFromFile()
227    {
228        Properties props = null;
229
230        try
231        {
232            props = RemoteUtils.loadProps(propsFileName);
233            registryHost = props.getProperty("registry.host", registryHost);
234            final String portS = props.getProperty("registry.port", String.valueOf(registryPort));
235            setRegistryPort(portS);
236        }
237        catch (final IOException e)
238        {
239            log.error("Problem loading props.", e);
240        }
241
242        return props;
243    }
244
245    /**
246     * Load configuration values from init params if possible
247     */
248    private void loadInitParams()
249    {
250        final ServletConfig config = getServletConfig();
251        final String _propsFileName = config.getInitParameter("propsFileName");
252        if (null != _propsFileName)
253        {
254            this.propsFileName = _propsFileName;
255        }
256        final String _registryHost = config.getInitParameter("registryHost");
257        if (null != _registryHost)
258        {
259            this.registryHost = _registryHost;
260        }
261        final String regPortString = config.getInitParameter("registryPort");
262        if (null != regPortString)
263        {
264            setRegistryPort(regPortString);
265        }
266    }
267
268    /**
269     * Set registry port from string If the string cannot be parsed, the default
270     * value is used
271     *
272     * @param portS
273     */
274    private void setRegistryPort(final String portS)
275    {
276        try
277        {
278            this.registryPort = Integer.parseInt(portS);
279        }
280        catch (final NumberFormatException e)
281        {
282            log.error("Problem converting port to an int.", e);
283            this.registryPort = DEFAULT_REGISTRY_PORT;
284        }
285    }
286}