001package org.apache.commons.jcs.auxiliary.remote;
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.util.ArrayList;
024
025import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
026import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
027import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
028import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
029import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
030import org.apache.commons.jcs.engine.stats.StatElement;
031import org.apache.commons.jcs.engine.stats.Stats;
032import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
033import org.apache.commons.jcs.engine.stats.behavior.IStats;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036
037/**
038 * Client proxy for an RMI remote cache.
039 * <p>
040 * This handles gets, updates, and removes. It also initiates failover recovery when an error is
041 * encountered.
042 */
043public class RemoteCache<K, V>
044    extends AbstractRemoteAuxiliaryCache<K, V>
045{
046    /** The logger. */
047    private static final Log log = LogFactory.getLog( RemoteCache.class );
048
049    /** for error notifications */
050    private RemoteCacheMonitor monitor;
051
052    /** back link for failover initiation */
053    private AbstractRemoteCacheNoWaitFacade<K, V> facade;
054
055    /**
056     * Constructor for the RemoteCache object. This object communicates with a remote cache server.
057     * One of these exists for each region. This also holds a reference to a listener. The same
058     * listener is used for all regions for one remote server. Holding a reference to the listener
059     * allows this object to know the listener id assigned by the remote cache.
060     * <p>
061     * @param cattr the cache configuration
062     * @param remote the remote cache server handle
063     * @param listener a listener
064     * @param monitor the cache monitor
065     */
066    public RemoteCache( IRemoteCacheAttributes cattr,
067        ICacheServiceNonLocal<K, V> remote,
068        IRemoteCacheListener<K, V> listener,
069        RemoteCacheMonitor monitor )
070    {
071        super( cattr, remote, listener );
072        this.monitor = monitor;
073
074        RemoteUtils.configureGlobalCustomSocketFactory( getRemoteCacheAttributes().getRmiSocketFactoryTimeoutMillis() );
075    }
076
077    /**
078     * @return IStats object
079     */
080    @Override
081    public IStats getStatistics()
082    {
083        IStats stats = new Stats();
084        stats.setTypeName( "Remote Cache" );
085
086        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
087
088        elems.add(new StatElement<String>( "Remote Host:Port", getIPAddressForService() ) );
089        elems.add(new StatElement<String>( "Remote Type", this.getRemoteCacheAttributes().getRemoteTypeName() ) );
090
091//      if ( this.getRemoteCacheAttributes().getRemoteType() == RemoteType.CLUSTER )
092//      {
093//          // something cluster specific
094//      }
095
096        // get the stats from the super too
097        IStats sStats = super.getStatistics();
098        elems.addAll(sStats.getStatElements());
099
100        stats.setStatElements( elems );
101
102        return stats;
103    }
104
105    /**
106     * Set facade
107     *
108     * @param facade the facade to set
109     */
110    protected void setFacade(AbstractRemoteCacheNoWaitFacade<K, V> facade)
111    {
112        this.facade = facade;
113    }
114
115    /**
116     * Get facade
117     *
118     * @return the facade
119     */
120    protected AbstractRemoteCacheNoWaitFacade<K, V> getFacade()
121    {
122        return facade;
123    }
124
125    /**
126     * Handles exception by disabling the remote cache service before re-throwing the exception in
127     * the form of an IOException.
128     * <p>
129     * @param ex
130     * @param msg
131     * @param eventName
132     * @throws IOException
133     */
134    @Override
135    protected void handleException( Exception ex, String msg, String eventName )
136        throws IOException
137    {
138        String message = "Disabling remote cache due to error: " + msg;
139
140        logError( cacheName, "", message );
141        log.error( message, ex );
142
143        // we should not switch if the existing is a zombie.
144        if ( getRemoteCacheService() == null || !( getRemoteCacheService() instanceof ZombieCacheServiceNonLocal ) )
145        {
146            // TODO make configurable
147            setRemoteCacheService( new ZombieCacheServiceNonLocal<K, V>( getRemoteCacheAttributes().getZombieQueueMaxSize() ) );
148        }
149        // may want to flush if region specifies
150        // Notify the cache monitor about the error, and kick off the recovery
151        // process.
152        monitor.notifyError();
153
154        if ( log.isDebugEnabled() )
155        {
156            log.debug( "Initiating failover, rcnwf = " + facade );
157        }
158
159        if ( facade != null && facade.getAuxiliaryCacheAttributes().getRemoteType() == RemoteType.LOCAL )
160        {
161            if ( log.isDebugEnabled() )
162            {
163                log.debug( "Found facade, calling failover" );
164            }
165            // may need to remove the noWait index here. It will be 0 if it is
166            // local since there is only 1 possible listener.
167            facade.failover( facade.getPrimaryServer() );
168        }
169
170        if ( ex instanceof IOException )
171        {
172            throw (IOException) ex;
173        }
174        throw new IOException( ex );
175    }
176
177    /**
178     * Debugging info.
179     * <p>
180     * @return basic info about the RemoteCache
181     */
182    @Override
183    public String toString()
184    {
185        return "RemoteCache: " + cacheName + " attributes = " + getRemoteCacheAttributes();
186    }
187
188    /**
189     * Gets the extra info for the event log.
190     * <p>
191     * @return disk location
192     */
193    @Override
194    public String getEventLoggingExtraInfo()
195    {
196        return getIPAddressForService();
197    }
198
199    /**
200     * IP address for the service, if one is stored.
201     * <p>
202     * Protected for testing.
203     * <p>
204     * @return String
205     */
206    protected String getIPAddressForService()
207    {
208        String ipAddress = "(null)";
209        if (this.getRemoteCacheAttributes().getRemoteLocation() != null)
210        {
211            ipAddress = this.getRemoteCacheAttributes().getRemoteLocation().toString();
212        }
213        return ipAddress;
214    }
215}