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}