| package org.apache.lucene.index; |
| |
| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.store.MockDirectoryWrapper; |
| import org.apache.lucene.util.Constants; |
| import org.apache.lucene.util._TestUtil; |
| |
| /** |
| * Runs TestNRTThreads in a separate process, crashes the JRE in the middle |
| * of execution, then runs checkindex to make sure its not corrupt. |
| */ |
| public class TestIndexWriterOnJRECrash extends TestNRTThreads { |
| private File tempDir; |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| tempDir = _TestUtil.getTempDir("jrecrash"); |
| tempDir.delete(); |
| tempDir.mkdir(); |
| } |
| |
| @Override |
| public void testNRTThreads() throws Exception { |
| String vendor = Constants.JAVA_VENDOR; |
| assumeTrue(vendor + " JRE not supported.", |
| vendor.startsWith("Oracle") || vendor.startsWith("Sun") || vendor.startsWith("Apple")); |
| |
| // if we are not the fork |
| if (System.getProperty("tests.crashmode") == null) { |
| // try up to 10 times to create an index |
| for (int i = 0; i < 10; i++) { |
| forkTest(); |
| // if we succeeded in finding an index, we are done. |
| if (checkIndexes(tempDir)) |
| return; |
| } |
| } else { |
| // we are the fork, setup a crashing thread |
| final int crashTime = _TestUtil.nextInt(random, 3000, 4000); |
| Thread t = new Thread() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(crashTime); |
| } catch (InterruptedException e) {} |
| crashJRE(); |
| } |
| }; |
| t.setPriority(Thread.MAX_PRIORITY); |
| t.start(); |
| // run the test until we crash. |
| for (int i = 0; i < 1000; i++) { |
| super.testNRTThreads(); |
| } |
| } |
| } |
| |
| /** fork ourselves in a new jvm. sets -Dtests.crashmode=true */ |
| public void forkTest() throws Exception { |
| List<String> cmd = new ArrayList<String>(); |
| cmd.add(System.getProperty("java.home") |
| + System.getProperty("file.separator") |
| + "bin" |
| + System.getProperty("file.separator") |
| + "java"); |
| cmd.add("-Xmx512m"); |
| cmd.add("-Dtests.crashmode=true"); |
| // passing NIGHTLY to this test makes it run for much longer, easier to catch it in the act... |
| cmd.add("-Dtests.nightly=true"); |
| cmd.add("-DtempDir=" + tempDir.getPath()); |
| cmd.add("-Dtests.seed=" + random.nextLong() + ":" + random.nextLong()); |
| cmd.add("-ea"); |
| cmd.add("-cp"); |
| cmd.add(System.getProperty("java.class.path")); |
| cmd.add("org.junit.runner.JUnitCore"); |
| cmd.add(getClass().getName()); |
| ProcessBuilder pb = new ProcessBuilder(cmd); |
| pb.directory(tempDir); |
| pb.redirectErrorStream(true); |
| Process p = pb.start(); |
| InputStream is = p.getInputStream(); |
| BufferedInputStream isl = new BufferedInputStream(is); |
| byte buffer[] = new byte[1024]; |
| int len = 0; |
| if (VERBOSE) System.err.println(">>> Begin subprocess output"); |
| while ((len = isl.read(buffer)) != -1) { |
| if (VERBOSE) { |
| System.err.write(buffer, 0, len); |
| } |
| } |
| if (VERBOSE) System.err.println("<<< End subprocess output"); |
| p.waitFor(); |
| } |
| |
| /** |
| * Recursively looks for indexes underneath <code>file</code>, |
| * and runs checkindex on them. returns true if it found any indexes. |
| */ |
| public boolean checkIndexes(File file) throws IOException { |
| if (file.isDirectory()) { |
| MockDirectoryWrapper dir = newFSDirectory(file); |
| dir.setCheckIndexOnClose(false); // don't double-checkindex |
| if (IndexReader.indexExists(dir)) { |
| if (VERBOSE) { |
| System.err.println("Checking index: " + file); |
| } |
| _TestUtil.checkIndex(dir); |
| dir.close(); |
| return true; |
| } |
| dir.close(); |
| for (File f : file.listFiles()) |
| if (checkIndexes(f)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * currently, this only works/tested on Sun and IBM. |
| */ |
| public void crashJRE() { |
| try { |
| Class<?> clazz = Class.forName("sun.misc.Unsafe"); |
| // we should use getUnsafe instead, harmony implements it, etc. |
| Field field = clazz.getDeclaredField("theUnsafe"); |
| field.setAccessible(true); |
| Object o = field.get(null); |
| Method m = clazz.getMethod("putAddress", long.class, long.class); |
| m.invoke(o, 0L, 0L); |
| } catch (Exception e) { e.printStackTrace(); } |
| fail(); |
| } |
| } |