1 package org.apache.commons.jcs3.auxiliary.remote;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.rmi.registry.Registry;
23 import java.util.ArrayList;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26
27 import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheFactory;
28 import org.apache.commons.jcs3.auxiliary.AuxiliaryCache;
29 import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes;
30 import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheAttributes;
31 import org.apache.commons.jcs3.auxiliary.remote.server.behavior.RemoteType;
32 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager;
33 import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
34 import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
35
36 /**
37 * The RemoteCacheFactory creates remote caches for the cache hub. It returns a no wait facade which
38 * is a wrapper around a no wait. The no wait object is either an active connection to a remote
39 * cache or a balking zombie if the remote cache is not accessible. It should be transparent to the
40 * clients.
41 */
42 public class RemoteCacheFactory
43 extends AbstractAuxiliaryCacheFactory
44 {
45 /** Monitor thread */
46 private RemoteCacheMonitor monitor;
47
48 /** Contains mappings of RemoteLocation instance to RemoteCacheManager instance. */
49 private ConcurrentMap<RemoteLocation, RemoteCacheManager> managers;
50
51 /**
52 * For LOCAL clients we get a handle to all the failovers, but we do not register a listener
53 * with them. We create the RemoteCacheManager, but we do not get a cache.
54 * <p>
55 * The failover runner will get a cache from the manager. When the primary is restored it will
56 * tell the manager for the failover to deregister the listener.
57 * <p>
58 * @param iaca
59 * @param cacheMgr
60 * @param cacheEventLogger
61 * @param elementSerializer
62 * @return AuxiliaryCache
63 */
64 @Override
65 public <K, V> AuxiliaryCache<K, V> createCache(
66 final AuxiliaryCacheAttributes iaca, final ICompositeCacheManager cacheMgr,
67 final ICacheEventLogger cacheEventLogger, final IElementSerializer elementSerializer )
68 {
69 final RemoteCacheAttributes rca = (RemoteCacheAttributes) iaca;
70
71 final ArrayList<RemoteCacheNoWait<K,V>> noWaits = new ArrayList<>();
72
73 switch (rca.getRemoteType())
74 {
75 case LOCAL:
76 // a list to be turned into an array of failover server information
77 final ArrayList<RemoteLocation> failovers = new ArrayList<>();
78
79 // not necessary if a failover list is defined
80 // REGISTER PRIMARY LISTENER
81 // if it is a primary
82 if ( rca.getRemoteLocation() != null )
83 {
84 failovers.add( rca.getRemoteLocation() );
85 final RemoteCacheManager rcm = getManager( rca, cacheMgr, cacheEventLogger, elementSerializer );
86 noWaits.add(rcm.getCache(rca));
87 }
88
89 // GET HANDLE BUT DON'T REGISTER A LISTENER FOR FAILOVERS
90 final String failoverList = rca.getFailoverServers();
91 if (failoverList != null && !failoverList.isEmpty())
92 {
93 final String[] failoverServers = failoverList.split("\\s*,\\s*");
94 for (String server : failoverServers)
95 {
96 final RemoteLocation location = RemoteLocation.parseServerAndPort(server);
97
98 if (location != null)
99 {
100 failovers.add( location );
101 final RemoteCacheAttributes frca = (RemoteCacheAttributes) rca.clone();
102 frca.setRemoteLocation(location);
103 final RemoteCacheManager rcm = getManager( frca, cacheMgr, cacheEventLogger, elementSerializer );
104
105 // add a listener if there are none, need to tell rca what
106 // number it is at
107 if (noWaits.isEmpty())
108 {
109 frca.setFailoverIndex(0);
110 noWaits.add(rcm.getCache(frca));
111 }
112 }
113 }
114 // end for
115 }
116 // end if failoverList != null
117
118 rca.setFailovers( failovers );
119 break;
120
121 case CLUSTER:
122 // REGISTER LISTENERS FOR EACH SYSTEM CLUSTERED CACHEs
123 final String[] clusterServers = rca.getClusterServers().split("\\s*,\\s*");
124 for (String server: clusterServers)
125 {
126 if (server.isEmpty())
127 {
128 continue;
129 }
130
131 final RemoteLocation location = RemoteLocation.parseServerAndPort(server);
132
133 if (location != null)
134 {
135 final RemoteCacheAttributes crca = (RemoteCacheAttributes) rca.clone();
136 crca.setRemoteLocation(location);
137 final RemoteCacheManager rcm = getManager( crca, cacheMgr, cacheEventLogger, elementSerializer );
138 crca.setRemoteType( RemoteType.CLUSTER );
139 noWaits.add(rcm.getCache(crca));
140 }
141 }
142 break;
143 }
144
145 return new RemoteCacheNoWaitFacade<>(noWaits, rca, cacheEventLogger, elementSerializer, this);
146 }
147 // end createCache
148
149 /**
150 * Returns an instance of RemoteCacheManager for the given connection parameters.
151 * <p>
152 * Host and Port uniquely identify a manager instance.
153 * <p>
154 * @param cattr
155 *
156 * @return The instance value or null if no such manager exists
157 */
158 public RemoteCacheManager getManager( final IRemoteCacheAttributes cattr )
159 {
160 final RemoteCacheAttributes rca = (RemoteCacheAttributes) cattr.clone();
161 if (rca.getRemoteLocation() == null)
162 {
163 rca.setRemoteLocation("", Registry.REGISTRY_PORT);
164 }
165
166 return managers.get(rca.getRemoteLocation());
167 }
168
169 /**
170 * Returns an instance of RemoteCacheManager for the given connection parameters.
171 * <p>
172 * Host and Port uniquely identify a manager instance.
173 * <p>
174 * If the connection cannot be established, zombie objects will be used for future recovery
175 * purposes.
176 * <p>
177 * @param cattr the cache configuration object
178 * @param cacheMgr the cache manager
179 * @param cacheEventLogger the event logger
180 * @param elementSerializer the serializer to use for sending and receiving
181 *
182 * @return The instance value, never null
183 */
184 public RemoteCacheManager getManager( final IRemoteCacheAttributes cattr,
185 final ICompositeCacheManager cacheMgr,
186 final ICacheEventLogger cacheEventLogger,
187 final IElementSerializer elementSerializer )
188 {
189 final RemoteCacheAttributes rca = (RemoteCacheAttributes) cattr.clone();
190 if (rca.getRemoteLocation() == null)
191 {
192 rca.setRemoteLocation("", Registry.REGISTRY_PORT);
193 }
194
195 return managers.computeIfAbsent(rca.getRemoteLocation(), key -> {
196
197 RemoteCacheManager manager = new RemoteCacheManager(rca, cacheMgr, monitor, cacheEventLogger, elementSerializer);
198 monitor.addManager(manager);
199
200 return manager;
201 });
202 }
203
204 /**
205 * @see org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheFactory#initialize()
206 */
207 @Override
208 public void initialize()
209 {
210 super.initialize();
211
212 managers = new ConcurrentHashMap<>();
213
214 monitor = new RemoteCacheMonitor();
215 monitor.setDaemon(true);
216 }
217
218 /**
219 * @see org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheFactory#dispose()
220 */
221 @Override
222 public void dispose()
223 {
224 managers.values().forEach(RemoteCacheManager::release);
225 managers.clear();
226
227 if (monitor != null)
228 {
229 monitor.notifyShutdown();
230 try
231 {
232 monitor.join(5000);
233 }
234 catch (final InterruptedException e)
235 {
236 // swallow
237 }
238 monitor = null;
239 }
240
241 super.dispose();
242 }
243 }