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.rmi.registry.Registry;
023import java.util.ArrayList;
024import java.util.StringTokenizer;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ConcurrentMap;
027import java.util.concurrent.locks.Lock;
028import java.util.concurrent.locks.ReentrantLock;
029
030import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheFactory;
031import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
032import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
033import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
034import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
035import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
036import org.apache.commons.jcs.engine.behavior.IElementSerializer;
037import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
038
039/**
040 * The RemoteCacheFactory creates remote caches for the cache hub. It returns a no wait facade which
041 * is a wrapper around a no wait. The no wait object is either an active connection to a remote
042 * cache or a balking zombie if the remote cache is not accessible. It should be transparent to the
043 * clients.
044 */
045public class RemoteCacheFactory
046    extends AbstractAuxiliaryCacheFactory
047{
048    /** Monitor thread */
049    private RemoteCacheMonitor monitor;
050
051    /** Contains mappings of RemoteLocation instance to RemoteCacheManager instance. */
052    private ConcurrentMap<RemoteLocation, RemoteCacheManager> managers;
053
054    /** Lock for initialization of manager instances */
055    private Lock managerLock;
056
057    /**
058     * For LOCAL clients we get a handle to all the failovers, but we do not register a listener
059     * with them. We create the RemoteCacheManager, but we do not get a cache.
060     * <p>
061     * The failover runner will get a cache from the manager. When the primary is restored it will
062     * tell the manager for the failover to deregister the listener.
063     * <p>
064     * @param iaca
065     * @param cacheMgr
066     * @param cacheEventLogger
067     * @param elementSerializer
068     * @return AuxiliaryCache
069     */
070    @Override
071    public <K, V> AuxiliaryCache<K, V> createCache(
072            AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
073           ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
074    {
075        RemoteCacheAttributes rca = (RemoteCacheAttributes) iaca;
076
077        ArrayList<RemoteCacheNoWait<K,V>> noWaits = new ArrayList<RemoteCacheNoWait<K,V>>();
078
079        switch (rca.getRemoteType())
080        {
081            case LOCAL:
082                // a list to be turned into an array of failover server information
083                ArrayList<RemoteLocation> failovers = new ArrayList<RemoteLocation>();
084
085                // not necessary if a failover list is defined
086                // REGISTER PRIMARY LISTENER
087                // if it is a primary
088                boolean primaryDefined = false;
089                if ( rca.getRemoteLocation() != null )
090                {
091                    primaryDefined = true;
092
093                    failovers.add( rca.getRemoteLocation() );
094                    RemoteCacheManager rcm = getManager( rca, cacheMgr, cacheEventLogger, elementSerializer );
095                    RemoteCacheNoWait<K,V> ic = rcm.getCache( rca );
096                    noWaits.add( ic );
097                }
098
099                // GET HANDLE BUT DONT REGISTER A LISTENER FOR FAILOVERS
100                String failoverList = rca.getFailoverServers();
101                if ( failoverList != null )
102                {
103                    StringTokenizer fit = new StringTokenizer( failoverList, "," );
104                    int fCnt = 0;
105                    while ( fit.hasMoreTokens() )
106                    {
107                        fCnt++;
108
109                        String server = fit.nextToken();
110                        RemoteLocation location = RemoteLocation.parseServerAndPort(server);
111
112                        if (location != null)
113                        {
114                            failovers.add( location );
115                            rca.setRemoteLocation(location);
116                            RemoteCacheManager rcm = getManager( rca, cacheMgr, cacheEventLogger, elementSerializer );
117
118                            // add a listener if there are none, need to tell rca what
119                            // number it is at
120                            if ( ( !primaryDefined && fCnt == 1 ) || noWaits.size() <= 0 )
121                            {
122                                RemoteCacheNoWait<K,V> ic = rcm.getCache( rca );
123                                noWaits.add( ic );
124                            }
125                        }
126                    }
127                    // end while
128                }
129                // end if failoverList != null
130
131                rca.setFailovers( failovers );
132                break;
133
134            case CLUSTER:
135                // REGISTER LISTENERS FOR EACH SYSTEM CLUSTERED CACHEs
136                StringTokenizer it = new StringTokenizer( rca.getClusterServers(), "," );
137                while ( it.hasMoreElements() )
138                {
139                    String server = (String) it.nextElement();
140                    RemoteLocation location = RemoteLocation.parseServerAndPort(server);
141
142                    if (location != null)
143                    {
144                        rca.setRemoteLocation(location);
145                        RemoteCacheManager rcm = getManager( rca, cacheMgr, cacheEventLogger, elementSerializer );
146                        rca.setRemoteType( RemoteType.CLUSTER );
147                        RemoteCacheNoWait<K,V> ic = rcm.getCache( rca );
148                        noWaits.add( ic );
149                    }
150                }
151                break;
152        }
153
154        RemoteCacheNoWaitFacade<K, V> rcnwf =
155            new RemoteCacheNoWaitFacade<K, V>(noWaits, rca, cacheEventLogger, elementSerializer, this );
156
157        return rcnwf;
158    }
159
160    // end createCache
161
162    /**
163     * Returns an instance of RemoteCacheManager for the given connection parameters.
164     * <p>
165     * Host and Port uniquely identify a manager instance.
166     * <p>
167     * @param cattr
168     *
169     * @return The instance value or null if no such manager exists
170     */
171    public RemoteCacheManager getManager( IRemoteCacheAttributes cattr )
172    {
173        if ( cattr.getRemoteLocation() == null )
174        {
175            cattr.setRemoteLocation("", Registry.REGISTRY_PORT);
176        }
177
178        RemoteLocation loc = cattr.getRemoteLocation();
179        RemoteCacheManager ins = managers.get( loc );
180
181        return ins;
182    }
183
184    /**
185     * Returns an instance of RemoteCacheManager for the given connection parameters.
186     * <p>
187     * Host and Port uniquely identify a manager instance.
188     * <p>
189     * If the connection cannot be established, zombie objects will be used for future recovery
190     * purposes.
191     * <p>
192     * @param cattr
193     * @param cacheMgr
194     * @param cacheEventLogger
195     * @param elementSerializer
196     * @return The instance value, never null
197     */
198    public RemoteCacheManager getManager( IRemoteCacheAttributes cattr, ICompositeCacheManager cacheMgr,
199                                                  ICacheEventLogger cacheEventLogger,
200                                                  IElementSerializer elementSerializer )
201    {
202        RemoteCacheManager ins = getManager( cattr );
203
204        if ( ins == null )
205        {
206            managerLock.lock();
207
208            try
209            {
210                ins = managers.get( cattr.getRemoteLocation() );
211
212                if (ins == null)
213                {
214                    ins = new RemoteCacheManager( cattr, cacheMgr, monitor, cacheEventLogger, elementSerializer);
215                    managers.put( cattr.getRemoteLocation(), ins );
216                    monitor.addManager(ins);
217                }
218            }
219            finally
220            {
221                managerLock.unlock();
222            }
223        }
224
225        return ins;
226    }
227
228        /**
229         * @see org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheFactory#initialize()
230         */
231        @Override
232        public void initialize()
233        {
234                super.initialize();
235
236                managers = new ConcurrentHashMap<RemoteLocation, RemoteCacheManager>();
237                managerLock = new ReentrantLock();
238
239        monitor = new RemoteCacheMonitor();
240        monitor.setDaemon(true);
241        }
242
243        /**
244         * @see org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheFactory#dispose()
245         */
246        @Override
247        public void dispose()
248        {
249                for (RemoteCacheManager manager : managers.values())
250                {
251                        manager.release();
252                }
253
254                managers.clear();
255
256        if (monitor != null)
257        {
258            monitor.notifyShutdown();
259            try
260            {
261                monitor.join(5000);
262            }
263            catch (InterruptedException e)
264            {
265                // swallow
266            }
267            monitor = null;
268        }
269
270                super.dispose();
271        }
272}