diff options
| author | Keith Wall <kwall@apache.org> | 2014-08-05 13:33:00 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2014-08-05 13:33:00 +0000 |
| commit | f1de9935453ef75423efe9f04a7d7a2ca8e41837 (patch) | |
| tree | 390882199118673c3fd7d0515970e234ce4e4831 /qpid/java | |
| parent | 34b9e6385180b17cd6b702a0344239d815c6b7de (diff) | |
| download | qpid-python-f1de9935453ef75423efe9f04a7d7a2ca8e41837.tar.gz | |
QPID-5962: [Java Broker] Prevent two or more BDB virtual host or virtual hosts nodes sharing the same JE environment path
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1615928 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java')
5 files changed, 248 insertions, 15 deletions
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistry.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistry.java new file mode 100644 index 0000000000..ad592ef1f6 --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistry.java @@ -0,0 +1,88 @@ +/* + * 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.qpid.server.store.berkeleydb; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.apache.qpid.server.store.StoreException; + +/** + * JE permits the same environment to opened for write many times from within the same JVM. + * The Java Broker needs to disallow this as the stores of many VHNs or many VH + */ +public class EnvHomeRegistry +{ + private static final EnvHomeRegistry _instance = new EnvHomeRegistry(); + private final Set<String> _canonicalNames = new HashSet<>(); + + public static final EnvHomeRegistry getInstance() + { + return _instance; + } + + // default for unit testing + EnvHomeRegistry() + { + super(); + } + + public synchronized void registerHome(final File home) throws StoreException + { + if (home == null) + { + throw new IllegalArgumentException("home parameter cannot be null"); + } + + String canonicalForm = getCanonicalForm(home); + if (_canonicalNames.contains(canonicalForm)) + { + throw new IllegalArgumentException("JE Home " + home + " is already in use"); + } + _canonicalNames.add(canonicalForm); + } + + + public synchronized void deregisterHome(final File home) throws StoreException + { + if (home == null) + { + throw new IllegalArgumentException("home parameter cannot be null"); + } + + String canonicalForm = getCanonicalForm(home); + _canonicalNames.remove(canonicalForm); + } + + private String getCanonicalForm(final File home) + { + try + { + return home.getCanonicalPath(); + } + catch (IOException e) + { + throw new StoreException("Failed to resolve " + home + " into canonical form", e); + } + } + +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java index 55966ebce4..6451bc6927 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java @@ -49,6 +49,7 @@ public class StandardEnvironmentFacade implements EnvironmentFacade private Environment _environment; private final Committer _committer; + private final File _environmentPath; public StandardEnvironmentFacade(StandardEnvironmentConfiguration configuration) { @@ -59,12 +60,12 @@ public class StandardEnvironmentFacade implements EnvironmentFacade LOGGER.info("Creating environment at environment path " + _storePath); } - File environmentPath = new File(_storePath); - if (!environmentPath.exists()) + _environmentPath = new File(_storePath); + if (!_environmentPath.exists()) { - if (!environmentPath.mkdirs()) + if (!_environmentPath.mkdirs()) { - throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " + throw new IllegalArgumentException("Environment path " + _environmentPath + " could not be read or created. " + "Ensure the path is correct and that the permissions are correct."); } } @@ -92,7 +93,20 @@ public class StandardEnvironmentFacade implements EnvironmentFacade envConfig.setExceptionListener(new LoggingAsyncExceptionListener()); - _environment = new Environment(environmentPath, envConfig); + EnvHomeRegistry.getInstance().registerHome(_environmentPath); + boolean success = false; + try + { + _environment = new Environment(_environmentPath, envConfig); + success = true; + } + finally + { + if (!success) + { + EnvHomeRegistry.getInstance().deregisterHome(_environmentPath); + } + } _committer = new CoalescingCommiter(name, this); _committer.start(); @@ -135,7 +149,14 @@ public class StandardEnvironmentFacade implements EnvironmentFacade } finally { - closeEnvironment(); + try + { + closeEnvironment(); + } + finally + { + EnvHomeRegistry.getInstance().deregisterHome(_environmentPath); + } } } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java index 20a6bf058c..4c98f9fb26 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java @@ -57,7 +57,23 @@ import com.sleepycat.je.Sequence; import com.sleepycat.je.SequenceConfig; import com.sleepycat.je.Transaction; import com.sleepycat.je.TransactionConfig; -import com.sleepycat.je.rep.*; +import com.sleepycat.je.rep.AppStateMonitor; +import com.sleepycat.je.rep.InsufficientAcksException; +import com.sleepycat.je.rep.InsufficientLogException; +import com.sleepycat.je.rep.InsufficientReplicasException; +import com.sleepycat.je.rep.NetworkRestore; +import com.sleepycat.je.rep.NetworkRestoreConfig; +import com.sleepycat.je.rep.NodeState; +import com.sleepycat.je.rep.NodeType; +import com.sleepycat.je.rep.RepInternal; +import com.sleepycat.je.rep.ReplicatedEnvironment; +import com.sleepycat.je.rep.ReplicationConfig; +import com.sleepycat.je.rep.ReplicationGroup; +import com.sleepycat.je.rep.ReplicationMutableConfig; +import com.sleepycat.je.rep.ReplicationNode; +import com.sleepycat.je.rep.RestartRequiredException; +import com.sleepycat.je.rep.StateChangeEvent; +import com.sleepycat.je.rep.StateChangeListener; import com.sleepycat.je.rep.util.DbPing; import com.sleepycat.je.rep.util.ReplicationGroupAdmin; import com.sleepycat.je.rep.utilint.HostPortPair; @@ -71,6 +87,7 @@ import org.codehaus.jackson.map.ObjectMapper; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.berkeleydb.CoalescingCommiter; +import org.apache.qpid.server.store.berkeleydb.EnvHomeRegistry; import org.apache.qpid.server.store.berkeleydb.EnvironmentFacade; import org.apache.qpid.server.store.berkeleydb.LoggingAsyncExceptionListener; import org.apache.qpid.server.util.DaemonThreadFactory; @@ -132,7 +149,6 @@ public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChan put(ReplicationConfig.LOG_FLUSH_TASK_INTERVAL, "1 min"); }}); - public static final String TYPE = "BDB-HA"; private static final String PERMITTED_NODE_LIST = "permittedNodes"; private final ReplicatedEnvironmentConfiguration _configuration; @@ -183,7 +199,20 @@ public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChan _groupChangeExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() + 1, new DaemonThreadFactory("Group-Change-Learner:" + _prettyGroupNodeName)); // create environment in a separate thread to avoid renaming of the current thread by JE - _environment = createEnvironment(true); + EnvHomeRegistry.getInstance().registerHome(_environmentDirectory); + boolean success = false; + try + { + _environment = createEnvironment(true); + success = true; + } + finally + { + if (!success) + { + EnvHomeRegistry.getInstance().deregisterHome(_environmentDirectory); + } + } populateExistingRemoteReplicationNodes(); _groupChangeExecutor.submit(new RemoteNodeStateLearner()); } @@ -234,8 +263,8 @@ public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChan public void close() { if (_state.compareAndSet(State.OPENING, State.CLOSING) || - _state.compareAndSet(State.OPEN, State.CLOSING) || - _state.compareAndSet(State.RESTARTING, State.CLOSING) ) + _state.compareAndSet(State.OPEN, State.CLOSING) || + _state.compareAndSet(State.RESTARTING, State.CLOSING) ) { try { @@ -258,7 +287,14 @@ public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChan } finally { - closeEnvironment(); + try + { + closeEnvironment(); + } + finally + { + EnvHomeRegistry.getInstance().deregisterHome(_environmentDirectory); + } } } finally @@ -714,13 +750,13 @@ public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChan String newMaster = admin.transferMaster(Collections.singleton(nodeName), MASTER_TRANSFER_TIMEOUT, TimeUnit.MILLISECONDS, true); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("The mastership has been transfered to " + newMaster); + LOGGER.debug("The mastership has been transferred to " + newMaster); } } catch (DatabaseException e) { - LOGGER.warn("Exception on transfering the mastership to " + _prettyGroupNodeName - + " Master transfer timeout : " + MASTER_TRANSFER_TIMEOUT, e); + LOGGER.warn("Exception on transferring the mastership to " + _prettyGroupNodeName + + " Master transfer timeout : " + MASTER_TRANSFER_TIMEOUT, e); throw e; } return null; diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistryTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistryTest.java new file mode 100644 index 0000000000..5de51df992 --- /dev/null +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/EnvHomeRegistryTest.java @@ -0,0 +1,68 @@ +/* + * 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.qpid.server.store.berkeleydb; + +import java.io.File; + +import junit.framework.TestCase; + +import org.apache.qpid.test.utils.QpidTestCase; + +public class EnvHomeRegistryTest extends TestCase +{ + + private final EnvHomeRegistry _ehr = new EnvHomeRegistry(); + + public void testDuplicateEnvHomeRejected() throws Exception + { + File home = new File(QpidTestCase.TMP_FOLDER, getName()); + + _ehr.registerHome(home); + try + { + _ehr.registerHome(home); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + } + + public void testUniqueEnvHomesAllowed() throws Exception + { + File home1 = new File(QpidTestCase.TMP_FOLDER, getName() + "1"); + File home2 = new File(QpidTestCase.TMP_FOLDER, getName() + "2"); + + _ehr.registerHome(home1); + _ehr.registerHome(home2); + } + + public void testReuseOfEnvHomesAllowed() throws Exception + { + File home = new File(QpidTestCase.TMP_FOLDER, getName() + "1"); + + _ehr.registerHome(home); + + _ehr.deregisterHome(home); + + _ehr.registerHome(home); + } +}
\ No newline at end of file diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java index 9521f7d85d..3403ec53dc 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java @@ -73,6 +73,26 @@ public class StandardEnvironmentFacadeTest extends QpidTestCase assertTrue("Environment is not valid", e.isValid()); } + public void testSecondEnvironmentFacadeUsingSamePathRejected() throws Exception + { + EnvironmentFacade ef = createEnvironmentFacade(); + assertNotNull("Environment should not be null", ef); + try + { + createEnvironmentFacade(); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + + ef.close(); + + EnvironmentFacade ef2 = createEnvironmentFacade(); + assertNotNull("Environment should not be null", ef2); + } + public void testClose() throws Exception { EnvironmentFacade ef = createEnvironmentFacade(); |
