View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.concurrent.atomic.AtomicInteger;
24  
25  import org.apache.lucene.search.Query;
26  import org.apache.maven.index.context.IndexingContext;
27  import org.apache.maven.index.expr.UserInputSearchExpression;
28  import org.junit.Test;
29  
30  import static org.junit.Assert.assertEquals;
31  import static org.junit.Assert.assertFalse;
32  import static org.junit.Assert.assertNotNull;
33  import static org.junit.Assert.assertNull;
34  
35  public class ConcurrentUseTest extends AbstractNexusIndexerTest {
36      public static final int THREAD_COUNT = 10;
37  
38      protected File repo = new File(getBasedir(), "src/test/repo");
39  
40      @Override
41      protected void prepareNexusIndexer(NexusIndexer nexusIndexer) throws Exception {
42          context = nexusIndexer.addIndexingContext("test-default", "test", repo, indexDir, null, null, DEFAULT_CREATORS);
43  
44          assertNull(context.getTimestamp()); // unknown upon creation
45  
46          nexusIndexer.scan(context);
47  
48          assertNotNull(context.getTimestamp());
49      }
50  
51      protected IndexUserThread createThread(final ArtifactInfo ai) {
52          // we search and modify same context concurrently
53          return new IndexUserThread(this, nexusIndexer, context, context, ai);
54      }
55  
56      @Test
57      public void testConcurrency() throws Exception {
58          IndexUserThread[] threads = new IndexUserThread[THREAD_COUNT];
59  
60          ArtifactInfo ai = new ArtifactInfo(
61                  "test-default", "org.apache.maven.indexer", "index-concurrent-artifact", "", null, "jar");
62  
63          for (int i = 0; i < THREAD_COUNT; i++) {
64              threads[i] = createThread(ai);
65  
66              threads[i].start();
67          }
68  
69          Thread.sleep(5000);
70  
71          boolean thereWereProblems = false;
72  
73          int totalAdded = 0;
74  
75          for (int i = 0; i < THREAD_COUNT; i++) {
76              threads[i].stopThread();
77  
78              threads[i].join();
79  
80              thereWereProblems = thereWereProblems || threads[i].hadProblem();
81  
82              totalAdded += threads[i].getAdded();
83          }
84  
85          assertFalse("Not all thread did clean job!", thereWereProblems);
86  
87          context.commit();
88  
89          // sleep more than bottleWarmer does, to be sure commit and reopen happened
90          // BottleWarmer sleeps 1000 millis
91          Thread.sleep(2000);
92  
93          //
94  
95          Query q = nexusIndexer.constructQuery(MAVEN.GROUP_ID, new UserInputSearchExpression(ai.getGroupId()));
96  
97          FlatSearchResponse result = nexusIndexer.searchFlat(new FlatSearchRequest(q, context));
98  
99          assertEquals("All added should be found after final commit!", totalAdded, result.getTotalHitsCount());
100     }
101 
102     // ==
103 
104     private static final AtomicInteger versionSource = new AtomicInteger(1);
105 
106     protected void addToIndex(final NexusIndexer nexusIndexer, final IndexingContext indexingContext)
107             throws IOException {
108         final ArtifactInfo artifactInfo = new ArtifactInfo(
109                 "test-default",
110                 "org.apache.maven.indexer",
111                 "index-concurrent-artifact",
112                 "1." + versionSource.getAndIncrement(),
113                 null,
114                 "jar");
115 
116         final ArtifactContext ac = new ArtifactContext(null, null, null, artifactInfo, artifactInfo.calculateGav());
117 
118         nexusIndexer.addArtifactToIndex(ac, indexingContext);
119     }
120 
121     protected void deleteFromIndex(final NexusIndexer nexusIndexer, final IndexingContext indexingContext)
122             throws IOException {
123         // TODO: delete some of those already added
124         // artifactInfo.version = "1." + String.valueOf( versionSource.getAndIncrement() );
125         //
126         // ac = new ArtifactContext( null, null, null, artifactInfo, artifactInfo.calculateGav() );
127         //
128         // nexusIndexer.deleteArtifactFromIndex( ac, indexingContext );
129         //
130         // deleted++;
131     }
132 
133     protected int readIndex(final NexusIndexer nexusIndexer, final IndexingContext indexingContext) throws IOException {
134         final Query q =
135                 nexusIndexer.constructQuery(MAVEN.GROUP_ID, new UserInputSearchExpression("org.apache.maven.indexer"));
136 
137         FlatSearchResponse result = nexusIndexer.searchFlat(new FlatSearchRequest(q, indexingContext));
138 
139         return result.getReturnedHitsCount();
140     }
141 
142     // ==
143 
144     public static class IndexUserThread extends Thread {
145         private final ConcurrentUseTest test;
146 
147         private final NexusIndexer nexusIndexer;
148 
149         private final IndexingContext searchIndexingContext;
150 
151         private final IndexingContext modifyIndexingContext;
152 
153         private boolean stopped = false;
154 
155         private int added = 0;
156 
157         private int deleted = 0;
158 
159         private int lastSearchHitCount = 0;
160 
161         private Throwable t;
162 
163         public IndexUserThread(
164                 final ConcurrentUseTest test,
165                 final NexusIndexer nexusIndexer,
166                 final IndexingContext searchIndexingContext,
167                 final IndexingContext modifyIndexingContext,
168                 ArtifactInfo artifactInfo) {
169             this.test = test;
170 
171             this.nexusIndexer = nexusIndexer;
172 
173             this.searchIndexingContext = searchIndexingContext;
174 
175             this.modifyIndexingContext = modifyIndexingContext;
176         }
177 
178         public int getAdded() {
179             return added;
180         }
181 
182         public int getDeleted() {
183             return deleted;
184         }
185 
186         public boolean hadProblem() {
187             return t != null;
188         }
189 
190         public int getLastSearchHitCount() {
191             return lastSearchHitCount;
192         }
193 
194         public void stopThread() throws IOException {
195             this.modifyIndexingContext.commit();
196 
197             this.stopped = true;
198         }
199 
200         public void run() {
201             while (!stopped) {
202                 if (System.currentTimeMillis() % 5 == 0) {
203                     try {
204                         test.addToIndex(nexusIndexer, modifyIndexingContext);
205 
206                         added++;
207                     } catch (Throwable e) {
208                         t = e;
209 
210                         e.printStackTrace();
211 
212                         throw new IllegalStateException("error", e);
213                     }
214                 }
215 
216                 if (System.currentTimeMillis() % 11 == 0) {
217                     try {
218                         // test.deleteFromIndex( nexusIndexer, modifyIndexingContext );
219                         // deleted++;
220                     } catch (Throwable e) {
221                         t = e;
222 
223                         throw new IllegalStateException("error", e);
224                     }
225                 }
226 
227                 try {
228                     lastSearchHitCount = test.readIndex(nexusIndexer, searchIndexingContext);
229                 } catch (Throwable e) {
230                     t = e;
231 
232                     throw new IllegalStateException("error", e);
233                 }
234             }
235         }
236     }
237 }