Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit 5107d26

Browse files
author
Clément Le Provost
committed
Share Index instances
Each `Client` keeps a map of already created indices, with the index name as the key and a weak reference to the index as a value. When asked for an index with the same name, it returns the already created one, unless it has been garbage-collected. NOTE: In the tests, running the garbage collector does not finalize the index, although it ought to work (it does on a simple example). However, the debugger is positive that the only reference to the object is the weak map. => We’ll assume it should work in the real world… :/
1 parent dedf32d commit 5107d26

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

algoliasearch/src/main/java/com/algolia/search/saas/Client.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.io.InputStreamReader;
4242
import java.io.OutputStreamWriter;
4343
import java.io.UnsupportedEncodingException;
44+
import java.lang.ref.WeakReference;
4445
import java.net.HttpURLConnection;
4546
import java.net.URL;
4647
import java.net.URLEncoder;
@@ -127,6 +128,8 @@ public int hashCode() {
127128
/** Thread pool used to run asynchronous requests. */
128129
protected ExecutorService searchExecutorService = Executors.newFixedThreadPool(4);
129130

131+
protected Map<String, WeakReference<Index>> indices = new HashMap<>();
132+
130133
// ----------------------------------------------------------------------
131134
// Initialization
132135
// ----------------------------------------------------------------------
@@ -312,11 +315,32 @@ public void setSearchTimeout(int searchTimeout) {
312315
*
313316
* @param indexName The name of the index.
314317
* @return A new proxy to the specified index.
318+
*
319+
* @deprecated You should now use {@link #getIndex(String)}, which re-uses instances with the same name.
315320
*/
316321
public Index initIndex(@NonNull String indexName) {
317322
return new Index(this, indexName);
318323
}
319324

325+
/**
326+
* Obtain a proxy to an Algolia index (no server call required by this method).
327+
*
328+
* @param indexName The name of the index.
329+
* @return A proxy to the specified index.
330+
*/
331+
public @NonNull Index getIndex(@NonNull String indexName) {
332+
Index index = null;
333+
WeakReference<Index> existingIndex = indices.get(indexName);
334+
if (existingIndex != null) {
335+
index = existingIndex.get();
336+
}
337+
if (index == null) {
338+
index = new Index(this, indexName);
339+
indices.put(indexName, new WeakReference<Index>(index));
340+
}
341+
return index;
342+
}
343+
320344
/**
321345
* Add a software library to the list of user agents.
322346
*

algoliasearch/src/offline/java/com/algolia/search/saas/OfflineClient.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.algolia.search.offline.core.Sdk;
3030

3131
import java.io.File;
32+
import java.lang.ref.WeakReference;
3233
import java.util.concurrent.ExecutorService;
3334
import java.util.concurrent.Executors;
3435

@@ -110,15 +111,44 @@ public OfflineClient(@NonNull Context context, @NonNull String applicationID, @N
110111
/**
111112
* Create a new index. Although this will always be an instance of {@link MirroredIndex}, mirroring is deactivated
112113
* by default.
114+
*
113115
* @param indexName the name of index
114116
* @return The newly created index.
117+
*
118+
* @deprecated You should now use {@link #getIndex(String)}, which re-uses instances with the same name.
115119
*/
116120
@Override
117121
public MirroredIndex initIndex(@NonNull String indexName)
118122
{
119123
return new MirroredIndex(this, indexName);
120124
}
121125

126+
/**
127+
* Obtain a mirrored index. Although this will always be an instance of {@link MirroredIndex}, mirroring is
128+
* deactivated by default.
129+
*
130+
* @param indexName The name of the index.
131+
* @return A proxy to the specified index.
132+
*/
133+
@Override
134+
public @NonNull MirroredIndex getIndex(@NonNull String indexName) {
135+
MirroredIndex index = null;
136+
WeakReference<Index> existingIndex = indices.get(indexName);
137+
if (existingIndex != null) {
138+
Index anIndex = existingIndex.get();
139+
if (anIndex != null && !(anIndex instanceof MirroredIndex)) {
140+
throw new IllegalStateException("An index with the same name but a different type has already been created");
141+
} else {
142+
index = (MirroredIndex)anIndex;
143+
}
144+
}
145+
if (index == null) {
146+
index = new MirroredIndex(this, indexName);
147+
indices.put(indexName, new WeakReference<Index>(index));
148+
}
149+
return index;
150+
}
151+
122152
/**
123153
* Get the path to directory where the local data is stored.
124154
*/
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2016 Algolia
3+
* http://www.algolia.com/
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.algolia.search.saas;
25+
26+
import android.annotation.SuppressLint;
27+
28+
import org.junit.Test;
29+
import org.mockito.internal.util.reflection.Whitebox;
30+
import org.robolectric.util.concurrent.RoboExecutorService;
31+
32+
import java.lang.ref.WeakReference;
33+
import java.util.Map;
34+
35+
import static org.junit.Assert.assertEquals;
36+
37+
/**
38+
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
39+
*/
40+
41+
@SuppressLint("DefaultLocale") //We use format for logging errors, locale issues are irrelevant
42+
public class ClientTest extends PowerMockTestCase {
43+
Client client;
44+
45+
@Override
46+
public void setUp() throws Exception {
47+
super.setUp();
48+
client = new Client(Helpers.app_id, Helpers.api_key);
49+
// WARNING: Robolectric cannot work with custom executors in `AsyncTask`, so we substitute the client's
50+
// executor with a Robolectric-compliant one.
51+
Whitebox.setInternalState(client, "searchExecutorService", new RoboExecutorService());
52+
}
53+
54+
@Override
55+
public void tearDown() throws Exception {
56+
}
57+
58+
@Test
59+
public void testIndexReuse() throws Exception {
60+
Map<String, WeakReference<Index>> indices = (Map<String, WeakReference<Index>>)Whitebox.getInternalState(client, "indices");
61+
final String indexName = "name";
62+
63+
// Ask for the same index twice and check that it is re-used.
64+
assertEquals(0, indices.size());
65+
Index index1 = client.getIndex(indexName);
66+
assertEquals(1, indices.size());
67+
Index index2 = client.getIndex(indexName);
68+
assertEquals(index1, index2);
69+
assertEquals(1, indices.size());
70+
71+
// Release the index and check that the reference is null.
72+
// NOTE: This ought to work (works with a simple test case)... but does not.
73+
// Keeping the code for the sake of completeness.
74+
/*
75+
index1 = null;
76+
index2 = null;
77+
System.gc();
78+
assertNull(indices.get(indexName).get());
79+
*/
80+
}
81+
}

0 commit comments

Comments
 (0)