001package org.apache.commons.jcs3.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.jcs3.auxiliary.remote.behavior.IRemoteCacheAttributes;
026import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheListener;
027import org.apache.commons.jcs3.auxiliary.remote.server.behavior.RemoteType;
028import org.apache.commons.jcs3.engine.ZombieCacheServiceNonLocal;
029import org.apache.commons.jcs3.engine.behavior.ICacheServiceNonLocal;
030import org.apache.commons.jcs3.engine.stats.StatElement;
031import org.apache.commons.jcs3.engine.stats.Stats;
032import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
033import org.apache.commons.jcs3.engine.stats.behavior.IStats;
034import org.apache.commons.jcs3.log.Log;
035import org.apache.commons.jcs3.log.LogManager;
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 = LogManager.getLog( RemoteCache.class );
048
049    /** for error notifications */
050    private final 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( final IRemoteCacheAttributes cattr,
067        final ICacheServiceNonLocal<K, V> remote,
068        final IRemoteCacheListener<K, V> listener,
069        final 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        final IStats stats = new Stats();
084        stats.setTypeName( "Remote Cache" );
085
086        final ArrayList<IStatElement<?>> elems = new ArrayList<>();
087
088        elems.add(new StatElement<>( "Remote Host:Port", getIPAddressForService() ) );
089        elems.add(new StatElement<>( "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        final 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(final 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( final Exception ex, final String msg, final String eventName )
136        throws IOException
137    {
138        final 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() instanceof ZombieCacheServiceNonLocal ) )
145        {
146            // TODO make configurable
147            setRemoteCacheService( new ZombieCacheServiceNonLocal<>( 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        log.debug( "Initiating failover, rcnwf = {0}", facade );
155
156        if ( facade != null && facade.getAuxiliaryCacheAttributes().getRemoteType() == RemoteType.LOCAL )
157        {
158            log.debug( "Found facade, calling failover" );
159            // may need to remove the noWait index here. It will be 0 if it is
160            // local since there is only 1 possible listener.
161            facade.failover( facade.getPrimaryServer() );
162        }
163
164        if ( ex instanceof IOException )
165        {
166            throw (IOException) ex;
167        }
168        throw new IOException( ex );
169    }
170
171    /**
172     * Debugging info.
173     * <p>
174     * @return basic info about the RemoteCache
175     */
176    @Override
177    public String toString()
178    {
179        return "RemoteCache: " + cacheName + " attributes = " + getRemoteCacheAttributes();
180    }
181
182    /**
183     * Gets the extra info for the event log.
184     * <p>
185     * @return disk location
186     */
187    @Override
188    public String getEventLoggingExtraInfo()
189    {
190        return getIPAddressForService();
191    }
192
193    /**
194     * IP address for the service, if one is stored.
195     * <p>
196     * Protected for testing.
197     * <p>
198     * @return String
199     */
200    protected String getIPAddressForService()
201    {
202        String ipAddress = "(null)";
203        if (this.getRemoteCacheAttributes().getRemoteLocation() != null)
204        {
205            ipAddress = this.getRemoteCacheAttributes().getRemoteLocation().toString();
206        }
207        return ipAddress;
208    }
209}