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.rmi.registry.Registry;
023import java.util.ArrayList;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.ConcurrentMap;
026
027import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheFactory;
028import org.apache.commons.jcs3.auxiliary.AuxiliaryCache;
029import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes;
030import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheAttributes;
031import org.apache.commons.jcs3.auxiliary.remote.server.behavior.RemoteType;
032import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager;
033import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
034import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
035
036/**
037 * The RemoteCacheFactory creates remote caches for the cache hub. It returns a no wait facade which
038 * is a wrapper around a no wait. The no wait object is either an active connection to a remote
039 * cache or a balking zombie if the remote cache is not accessible. It should be transparent to the
040 * clients.
041 */
042public class RemoteCacheFactory
043    extends AbstractAuxiliaryCacheFactory
044{
045    /** Monitor thread */
046    private RemoteCacheMonitor monitor;
047
048    /** Contains mappings of RemoteLocation instance to RemoteCacheManager instance. */
049    private ConcurrentMap<RemoteLocation, RemoteCacheManager> managers;
050
051    /**
052     * For LOCAL clients we get a handle to all the failovers, but we do not register a listener
053     * with them. We create the RemoteCacheManager, but we do not get a cache.
054     * <p>
055     * The failover runner will get a cache from the manager. When the primary is restored it will
056     * tell the manager for the failover to deregister the listener.
057     * <p>
058     * @param iaca
059     * @param cacheMgr
060     * @param cacheEventLogger
061     * @param elementSerializer
062     * @return AuxiliaryCache
063     */
064    @Override
065    public <K, V> AuxiliaryCache<K, V> createCache(
066            final AuxiliaryCacheAttributes iaca, final ICompositeCacheManager cacheMgr,
067           final ICacheEventLogger cacheEventLogger, final IElementSerializer elementSerializer )
068    {
069        final RemoteCacheAttributes rca = (RemoteCacheAttributes) iaca;
070
071        final ArrayList<RemoteCacheNoWait<K,V>> noWaits = new ArrayList<>();
072
073        switch (rca.getRemoteType())
074        {
075            case LOCAL:
076                // a list to be turned into an array of failover server information
077                final ArrayList<RemoteLocation> failovers = new ArrayList<>();
078
079                // not necessary if a failover list is defined
080                // REGISTER PRIMARY LISTENER
081                // if it is a primary
082                if ( rca.getRemoteLocation() != null )
083                {
084                    failovers.add( rca.getRemoteLocation() );
085                    final RemoteCacheManager rcm = getManager( rca, cacheMgr, cacheEventLogger, elementSerializer );
086                    noWaits.add(rcm.getCache(rca));
087                }
088
089                // GET HANDLE BUT DON'T REGISTER A LISTENER FOR FAILOVERS
090                final String failoverList = rca.getFailoverServers();
091                if (failoverList != null && !failoverList.isEmpty())
092                {
093                    final String[] failoverServers = failoverList.split("\\s*,\\s*");
094                    for (String server : failoverServers)
095                    {
096                        final RemoteLocation location = RemoteLocation.parseServerAndPort(server);
097
098                        if (location != null)
099                        {
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}