You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2008/02/26 22:49:14 UTC

svn commit: r631397 - in /jackrabbit/branches/1.3: ./ jackrabbit-core/ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/

Author: jukka
Date: Tue Feb 26 13:49:05 2008
New Revision: 631397

URL: http://svn.apache.org/viewvc?rev=631397&view=rev
Log:
JCR-1400: Backport JCR-940: add db connection autoConnect for BundleDbPersistenceManager
    - Added test case from JCR-1419
    - Patch by Alexander Klimetschek

Added:
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/DatabaseConnectionFailureTest.java
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyProcess.java
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyTest.java
Modified:
    jackrabbit/branches/1.3/jackrabbit-core/pom.xml
    jackrabbit/branches/1.3/pom.xml

Modified: jackrabbit/branches/1.3/jackrabbit-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/pom.xml?rev=631397&r1=631396&r2=631397&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/pom.xml (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/pom.xml Tue Feb 26 13:49:05 2008
@@ -327,6 +327,16 @@
       <artifactId>slf4j-log4j12</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derbynet</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derbyclient</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Added: jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/DatabaseConnectionFailureTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/DatabaseConnectionFailureTest.java?rev=631397&view=auto
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/DatabaseConnectionFailureTest.java (added)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/DatabaseConnectionFailureTest.java Tue Feb 26 13:49:05 2008
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.core.persistence;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.core.persistence.ext.ExternalDerbyTest;
+
+/**
+ * <code>DatabaseConnectionFailureTest</code> tests various situations in
+ * which the connection of a persistence manager or file system to its database
+ * is lost. If the server restarts properly, Jackrabbit should re-connect
+ * automatically and in the best case there is no data lost.
+ */
+public class DatabaseConnectionFailureTest extends ExternalDerbyTest {
+
+	// this test takes about 2 mins and is not very important (it verifies
+	// that an RepositoryException is thrown if the database server behind
+	// the persistence manager is killed)
+/*
+	// external derby process
+	public void testConnectionBroken() throws Exception {
+		startExternalDerby();
+		
+		startJackrabbitWithExternalDerby();
+		Session session = helper.getSuperuserSession();
+		
+		// do something jcr-like
+		jcrWorkA(session);
+		session.save();
+		
+		killExternalDerby();
+		
+		// do something jcr-like => expect RepositoryException
+		jcrWorkB(session);
+		
+		long start = System.currentTimeMillis();
+		try {
+			// with the auto-reconnect feature in Bundle PMs, this save will trigger
+			// a loop of connection trials that will all fail, because we killed
+			// the server. this typically takes about 2 mins before finally a
+			// RepositoryException is thrown.
+			session.save();
+			
+			assertTrue("RepositoryException was expected (waiting some time is normal)", false);
+		} catch (RepositoryException e) {
+			// fine, exception is expected
+		}
+		long end = System.currentTimeMillis();
+		logger.debug("time taken: " + (end - start));
+	}
+*/
+	
+	// external derby process
+	public void testConnectionBrokenAndReconnect() throws Exception {
+		startExternalDerby();
+		
+		startJackrabbitWithExternalDerby();
+		Session session = helper.getSuperuserSession();
+		
+		// do something jcr-like
+		jcrWorkA(session);
+		session.save();
+		
+		killExternalDerby();
+		
+		// RESTART derby
+		startExternalDerby();
+		
+		// do something jcr-like => works again, maybe data corrupted
+		jcrWorkB(session);
+		
+		// an exception here means that the PM does not properly re-connect to the database
+		session.save();
+	}
+
+	// The following test cases are just ideas for testing an embedded derby
+/*	
+	// embedded derby
+	public void testConnectionClosed() throws Exception {
+		// start derby
+		// start jackrabbit + derby pm/file system
+		// do something jcr-like
+		// SHUTDOWN derby
+		// do something jcr-like => expect RepositoryException
+	}
+
+	// embedded derby
+	public void testConnectionClosedAndReconnect() throws Exception {
+		// start derby
+		// start jackrabbit + derby pm/file system
+		// do something jcr-like
+		// SHUTDOWN derby
+		// RESTART derby
+		// do something jcr-like => everything should work normally
+	}
+*/
+}

Added: jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java?rev=631397&view=auto
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java (added)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java Tue Feb 26 13:49:05 2008
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.core.persistence;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all jackrabbit specific testcases for the
+ * persistence module.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("Persistence tests");
+
+        suite.addTestSuite(DatabaseConnectionFailureTest.class);
+
+        return suite;
+    }
+}

Added: jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyProcess.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyProcess.java?rev=631397&view=auto
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyProcess.java (added)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyProcess.java Tue Feb 26 13:49:05 2008
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.core.persistence.ext;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.derby.drda.NetworkServerControl;
+import org.apache.derby.jdbc.EmbeddedDriver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>ExternalDerbyProcess</code> is a helper class for starting
+ * an external derby server on localhost, port 1527 (standard settings).
+ *
+ */
+public class ExternalDerbyProcess {
+	
+	public static Logger logger = LoggerFactory.getLogger(ExternalDerbyProcess.class);
+	
+	public static void setLogger(Logger aLogger) {
+		logger = aLogger;
+	}
+	
+	/**
+	 * Returns the path to the JAR file that a certain class is located in. This only works
+	 * if the classloader loaded this class from a JAR file.
+	 */
+	public static final String getJarFileForClass(Class clazz) {
+		// eg. /org/apache/derby/drda/NetworkServerControl.class
+		String classResource = "/" + clazz.getCanonicalName().replace(".", "/") + ".class";
+		// eg. jar:file:/Users/alex/.m2/repository/org/apache/derby/derbynet/10.2.1.6/derbynet-10.2.1.6.jar!/org/apache/derby/drda/NetworkServerControl.class
+		String fullResourceURL = clazz.getResource(classResource).toString();
+		// eg. /Users/alex/.m2/repository/org/apache/derby/derbynet/10.2.1.6/derbynet-10.2.1.6.jar
+		return fullResourceURL.replaceFirst("jar:file:([^!]+).*", "$1");
+	}
+	
+	public static final String EXTERNAL_DERBY_JDBC_DRIVER = "org.apache.derby.jdbc.ClientDriver";
+	
+	public static final String EXTERNAL_DERBY_USER = "cloud";
+	
+	public static final String EXTERNAL_DERBY_PASSWORD = "scape";
+	
+	private static final String DERBY_JAVA_CMD =
+		"-Dderby.drda.logConnections=true org.apache.derby.drda.NetworkServerControl start";
+	
+	private static final int DERBY_STARTUP_TIME = 1000; // ms
+
+	private static List processes = new ArrayList();
+	
+	public static Process start() throws IOException, InterruptedException {
+		// Let's create a hand-made pid
+		final String prefix = "[derby server " + (processes.size() + 1) + "]: ";
+		
+		// derby server needs classpath with:
+		// org.apache.derby:derby
+		// org.apache.derby:derbynet
+		String derbyJar = getJarFileForClass(EmbeddedDriver.class);
+		String derbyNetJar = getJarFileForClass(NetworkServerControl.class);
+		
+		String classPath = System.getProperty("java.class.path");
+		classPath += File.pathSeparatorChar + derbyJar;
+		classPath += File.pathSeparatorChar + derbyNetJar;
+		
+		String cmd = "java -cp " + classPath + " " + DERBY_JAVA_CMD;
+		logger.info(prefix + "Starting " + cmd);
+		
+		final Process p = Runtime.getRuntime().exec(cmd);
+		processes.add(p);
+		
+		// log stdout and stderr to our console
+		Thread stdoutThread = new Thread(new InputStream2ConsolePrinter(p.getInputStream(), prefix, false));
+		Thread stderrThread = new Thread(new InputStream2ConsolePrinter(p.getErrorStream(), prefix, true));
+		stdoutThread.start();
+		stderrThread.start();
+
+		// write the exit code on the console when the process ends
+		Thread exitCodeThread = new Thread(new Runnable() {
+			public void run() {
+				try {
+					logger.debug(prefix + "Exit code: " + p.waitFor());
+				} catch (InterruptedException e) {
+					logger.error("Interrupted while waiting for external derby process", e);
+				}
+			}
+		});
+		exitCodeThread.start();
+		
+		// wait for the server to start
+		Thread.sleep(DERBY_STARTUP_TIME);
+		
+		return p;
+	}
+	
+	public static void killAll() {
+		for (int i=0; i < processes.size(); i++) {
+			logger.debug("Killing derby server " + i);
+			((Process) processes.get(i)).destroy();
+		}
+	}
+
+	private static class InputStream2ConsolePrinter implements Runnable {
+		
+		private BufferedReader input;
+		private String prefix;
+		private boolean toStdErr;
+		
+		public InputStream2ConsolePrinter(InputStream is, String prefix, boolean toStdErr) {
+			this.input = new BufferedReader(new InputStreamReader(is));
+			this.prefix = prefix;
+			this.toStdErr = toStdErr;
+		}
+
+		public void run() {
+			String line;
+			try {
+				while ((line = input.readLine()) != null) {
+					if (toStdErr) {
+						logger.warn(prefix + line);
+					} else {
+						logger.debug(prefix + line);
+					}
+				}
+				input.close();
+			} catch (IOException e) {
+			}
+		}
+		
+	}
+	
+}

Added: jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyTest.java?rev=631397&view=auto
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyTest.java (added)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/ext/ExternalDerbyTest.java Tue Feb 26 13:49:05 2008
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package org.apache.jackrabbit.core.persistence.ext;
+
+import java.io.File;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.test.JUnitTest;
+import org.apache.jackrabbit.test.RepositoryHelper;
+import org.apache.jackrabbit.test.config.DynamicRepositoryHelper;
+import org.apache.jackrabbit.test.config.PersistenceManagerConf;
+import org.apache.jackrabbit.test.config.RepositoryConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>ExternalDerbyTest</code> is an abstract base class for jackrabbit
+ * persistence manager tests that need to connect to an external derby
+ * server for testing connection problems, data loss etc., which can
+ * be easily done by manipulating the external process.
+ * 
+ * This test class does not use {@link #setUp()} to setup the repository, but
+ * instead it is required to call {@link #startExternalDerby()} and
+ * {@link #startJackrabbitWithExternalDerby()}.
+ */
+public abstract class ExternalDerbyTest extends JUnitTest {
+	
+    /**
+     * Helper object to access repository transparently
+     */
+    public static RepositoryHelper helper = new RepositoryHelper();
+    
+	private static final String REPO_HOME = "target/repository";
+
+    protected final Logger logger = LoggerFactory.getLogger(JUnitTest.class);
+    
+	protected Process derbyProcess;
+
+	protected void setUp() {
+		// do nothing here, ie. don't set up helper with a new repo
+	}
+
+	protected void tearDown() {
+		killExternalDerby();
+	}
+
+	protected void startExternalDerby() throws Exception {
+		logger.debug("Starting external derby server...");
+		
+		ExternalDerbyProcess.setLogger(logger);
+		derbyProcess = ExternalDerbyProcess.start();
+	
+		try {
+			int exitCode = derbyProcess.exitValue();
+			throw new Exception("Derby server for testing did not start properly, exit code is " + exitCode);
+		} catch (IllegalThreadStateException e) {
+			// happens when the process is still running, which is good
+		}
+	}
+
+	protected void killExternalDerby() {
+		if (derbyProcess != null) {
+			try {
+				derbyProcess.exitValue();
+				// if there is no exception, derby has quit already
+				return;
+			} catch (IllegalThreadStateException e) {
+				// happens when the process is still running
+			}
+			
+			logger.debug("Killing external derby server...");
+			derbyProcess.destroy();
+		}
+	}
+	
+	protected RepositoryConf createRepositoryConf() {
+		RepositoryConf conf = new RepositoryConf();
+		
+		// set jdbc urls on PMs for external derby
+		// workspaces
+		PersistenceManagerConf pmc = conf.getWorkspaceConfTemplate().getPersistenceManagerConf();
+		pmc.setParameter("url", "jdbc:derby://localhost/${wsp.home}/version/db/itemState;create=true");
+		pmc.setParameter("driver", ExternalDerbyProcess.EXTERNAL_DERBY_JDBC_DRIVER);
+		pmc.setParameter("user", ExternalDerbyProcess.EXTERNAL_DERBY_USER);
+		pmc.setParameter("password", ExternalDerbyProcess.EXTERNAL_DERBY_PASSWORD);
+		// false is the default value anyway, but we want to make sure, the code does not block forever
+		pmc.setParameter("blockOnConnectionLoss", "false");
+		
+		// versioning
+		pmc = conf.getVersioningConf().getPersistenceManagerConf();
+		pmc.setParameter("url", "jdbc:derby://localhost/${rep.home}/db/itemState;create=true");
+		pmc.setParameter("driver", ExternalDerbyProcess.EXTERNAL_DERBY_JDBC_DRIVER);
+		pmc.setParameter("user", ExternalDerbyProcess.EXTERNAL_DERBY_USER);
+		pmc.setParameter("password", ExternalDerbyProcess.EXTERNAL_DERBY_PASSWORD);
+		// false is the default value anyway, but we want to make sure, the code does not block forever
+		pmc.setParameter("blockOnConnectionLoss", "false");
+
+		return conf;
+	}
+
+	protected void startJackrabbitWithExternalDerby() throws RepositoryException {
+		// clean up
+		shutdownJackrabbit();
+		deleteDirectory(new File(REPO_HOME));
+		
+		logger.debug("Starting jackrabbit...");
+		helper = new DynamicRepositoryHelper(createRepositoryConf(), REPO_HOME);
+	}
+
+	protected void shutdownJackrabbit() {
+		if (helper != null && helper instanceof DynamicRepositoryHelper) {
+			logger.debug("Stopping jackrabbit...");
+			((DynamicRepositoryHelper) helper).shutdown();
+		}
+	}
+
+	protected void jcrWorkA(Session session) throws RepositoryException {
+		Node rootNode = session.getRootNode();
+		Node node = rootNode.addNode("test");
+		node.setProperty("prop", "foobar");
+	}
+
+	protected void jcrWorkB(Session session) throws RepositoryException {
+		Node rootNode = session.getRootNode();
+		Node node = rootNode.addNode("test2");
+		node.setProperty("prop", "foobar");
+	}
+
+	public static boolean deleteDirectory(File path) {
+		if (path.exists()) {
+			File[] files = path.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				if (files[i].isDirectory()) {
+					deleteDirectory(files[i]);
+				} else {
+					files[i].delete();
+				}
+			}
+		}
+		return (path.delete());
+	}
+
+}

Modified: jackrabbit/branches/1.3/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/pom.xml?rev=631397&r1=631396&r2=631397&view=diff
==============================================================================
--- jackrabbit/branches/1.3/pom.xml (original)
+++ jackrabbit/branches/1.3/pom.xml Tue Feb 26 13:49:05 2008
@@ -693,6 +693,16 @@
         <version>10.2.1.6</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.derby</groupId>
+        <artifactId>derbynet</artifactId>
+        <version>10.2.1.6</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.derby</groupId>
+        <artifactId>derbyclient</artifactId>
+        <version>10.2.1.6</version>
+      </dependency>
+      <dependency>
         <groupId>poi</groupId>
         <artifactId>poi</artifactId>
         <version>2.5.1-final-20040804</version>