/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wfs.v2_0; import static org.junit.Assert.*; import org.custommonkey.xmlunit.XMLAssert; import org.geoserver.data.test.SystemTestData; import org.geotools.filter.v2_0.FES; import org.geotools.wfs.v2_0.WFS; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class GetFeatureWithLockTest extends WFS20TestSupport { @Override protected void setUpInternal(SystemTestData data) throws Exception { getServiceDescriptor20().getOperations().add( "ReleaseLock"); } @Test public void testPOST() throws Exception { String xml = "<wfs:GetFeatureWithLock service='WFS' version='2.0.0' " + "handle='GetFeatureWithLock-tc1' expiry='50' resultType='results' " + "xmlns:sf='http://cite.opengeospatial.org/gmlsf' xmlns:wfs='" + WFS.NAMESPACE + "'>" + "<wfs:Query handle='qry-1' typeNames='sf:PrimitiveGeoFeature' />" + "</wfs:GetFeatureWithLock>"; Document dom = postAsDOM("wfs", xml); assertGML32(dom); assertNotNull( dom.getDocumentElement().getAttribute("lockId") ); } @Test public void testUpdateLockedFeatureWithLockId() throws Exception { // get feature String xml = "<wfs:GetFeature service='WFS' version='2.0.0' expiry='100' " + "xmlns:cdf='http://www.opengis.net/cite/data' " + "xmlns:fes='" + FES.NAMESPACE + "' xmlns:wfs='" + WFS.NAMESPACE + "'>" + "<wfs:Query typeNames='cdf:Locks'/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("wfs", xml); assertGML32(dom); // get a fid assertFalse(dom.getElementsByTagName("cdf:Locks").getLength() == 0); String fid = ((Element) dom.getElementsByTagName("cdf:Locks").item(0)).getAttribute("gml:id"); // lock a feature xml = "<wfs:GetFeatureWithLock service='WFS' version='2.0.0' expiry='100' " + "xmlns:cdf='http://www.opengis.net/cite/data' " + "xmlns:fes='" + FES.NAMESPACE + "' xmlns:wfs='" + WFS.NAMESPACE + "'>" + "<wfs:Query typeNames='cdf:Locks'>" + "<fes:Filter><fes:ResourceId rid='" + fid + "'/>" + "</fes:Filter>" + "</wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("wfs", xml); assertGML32(dom); String lockId = dom.getDocumentElement().getAttribute("lockId"); // try to update it xml = "<wfs:Transaction " + " service=\"WFS\" " + " version=\"2.0.0\" " + " xmlns:cdf=\"http://www.opengis.net/cite/data\" " + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "' " + "> " + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"cdf:Locks\" handle='foo'> " + " <wfs:Property> " + " <wfs:ValueReference>cdf:id</wfs:ValueReference> " + " <wfs:Value>gfwlbt0001</wfs:Value> " + " </wfs:Property> " + " <fes:Filter> " + " <fes:ResourceId rid=\"" + fid + "\"/> " + " </fes:Filter> " + " </wfs:Update> " + "</wfs:Transaction> "; dom = postAsDOM("wfs", xml); // release the lock get("wfs?request=ReleaseLock&version=2.0.0&lockId=" + lockId); assertEquals("wfs:TransactionResponse", dom.getDocumentElement() .getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid + "']", dom); } @Test public void testUpdateLockedFeatureWithoutLockId() throws Exception { // get feature String xml = "<wfs:GetFeature " + "service=\"WFS\" " + "version=\"2.0.0\" " + "expiry=\"100\" " + "xmlns:cdf=\"http://www.opengis.net/cite/data\" " + "xmlns:fes='" + FES.NAMESPACE + "' " + "xmlns:wfs='" + WFS.NAMESPACE + "'>" + "<wfs:Query typeNames=\"cdf:Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("wfs", xml); // get a fid assertEquals("wfs:FeatureCollection", dom.getDocumentElement() .getNodeName()); assertFalse(dom.getElementsByTagName("cdf:Locks").getLength() == 0); String fid = ((Element) dom.getElementsByTagName("cdf:Locks").item(0)) .getAttribute("gml:id"); // lock a feature xml = "<wfs:GetFeatureWithLock " + "service=\"WFS\" " + "version=\"2.0.0\" " + "expiry=\"100\" " + "xmlns:cdf=\"http://www.opengis.net/cite/data\" " + "xmlns:fes='" + FES.NAMESPACE + "' " + "xmlns:wfs='" + WFS.NAMESPACE + "'>" + "<wfs:Query typeNames=\"cdf:Locks\">" + "<fes:Filter>" + "<fes:ResourceId rid=\"" + fid + "\"/>" + "</fes:Filter>" + "</wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement() .getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); // try to update it xml = "<wfs:Transaction " + " service=\"WFS\" " + " version=\"2.0.0\" " + " xmlns:cdf=\"http://www.opengis.net/cite/data\" " + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "' " + "> " + " <wfs:Update typeName=\"cdf:Locks\"> " + " <wfs:Property> " + " <wfs:ValueReference>cdf:id</wfs:ValueReference> " + " <wfs:Value>gfwlbt0001</wfs:Value> " + " </wfs:Property> " + " <fes:Filter> " + " <fes:ResourceId rid=\"" + fid + "\"/> " + " </fes:Filter> " + " </wfs:Update> " + "</wfs:Transaction> "; dom = postAsDOM("wfs", xml); // release the lock get("wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); assertEquals("ows:ExceptionReport", dom.getDocumentElement() .getNodeName()); XMLAssert.assertXpathExists("//ows:Exception[@exceptionCode = 'MissingParameterValue']", dom); } @Test public void testGetFeatureWithLockReleaseActionSome() throws Exception { String xml = "<wfs:GetFeature" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"cdf:Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("wfs", xml); // get two fids NodeList locks = dom.getElementsByTagName("cdf:Locks"); String fid1 = ((Element) locks.item(0)).getAttribute("gml:id"); String fid2 = ((Element) locks.item(1)).getAttribute("gml:id"); xml = "<wfs:GetFeatureWithLock" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"cdf:Locks\">" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement() .getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " releaseAction=\"SOME\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"cdf:Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0003</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("wfs", xml); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid1 + "']", dom); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"cdf:Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0004</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("wfs", xml); // release locks get("wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid2 + "']", dom); } @Test public void testWorkspaceQualified() throws Exception { String xml = "<wfs:GetFeature" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("cdf/wfs", xml); // get two fids NodeList locks = dom.getElementsByTagName("cdf:Locks"); String fid1 = ((Element) locks.item(0)).getAttribute("gml:id"); String fid2 = ((Element) locks.item(1)).getAttribute("gml:id"); xml = "<wfs:GetFeatureWithLock" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\">" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("cdf/wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); //System.out.println(lockId); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " releaseAction=\"SOME\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0003</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("cdf/wfs", xml); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid1 + "']", dom); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0004</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("cdf/wfs", xml); // release locks get("cdf/wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid2 + "']", dom); } @Test public void testLayerQualified() throws Exception { String xml = "<wfs:GetFeature" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("cdf/Locks/wfs", xml); // get two fids NodeList locks = dom.getElementsByTagName("cdf:Locks"); String fid1 = ((Element) locks.item(0)).getAttribute("gml:id"); String fid2 = ((Element) locks.item(1)).getAttribute("gml:id"); xml = "<wfs:GetFeatureWithLock" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"100\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\">" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("cdf/Fifteen/wfs", xml); XMLAssert.assertXpathEvaluatesTo("1", "count(//ows:ExceptionReport)", dom); dom = postAsDOM("cdf/Locks/wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); //System.out.println(lockId); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " releaseAction=\"SOME\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0003</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("cdf/Locks/wfs", xml); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid1 + "']", dom); xml = "<wfs:Transaction" + " service=\"WFS\"" + " version=\"2.0.0\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:LockId>" + lockId + "</wfs:LockId>" + " <wfs:Update typeName=\"Locks\">" + " <wfs:Property>" + " <wfs:ValueReference>cdf:id</wfs:ValueReference>" + " <wfs:Value>gfwlrs0004</wfs:Value>" + " </wfs:Property>" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Update>" + "</wfs:Transaction>"; dom = postAsDOM("cdf/Locks/wfs", xml); // release locks get("cdf/Locks/wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); assertEquals("wfs:TransactionResponse", dom.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("1", "//wfs:totalUpdated/text()", dom); XMLAssert.assertXpathExists("//wfs:UpdateResults//fes:ResourceId[@rid = '" + fid2 + "']", dom); } @Test public void testLockTwice() throws Exception { String xml = "<wfs:GetFeature" + " service=\"WFS\"" + " version=\"2.0.0\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("cdf/Locks/wfs", xml); // get two fids NodeList locks = dom.getElementsByTagName("cdf:Locks"); String fid1 = ((Element) locks.item(0)).getAttribute("gml:id"); String fid2 = ((Element) locks.item(1)).getAttribute("gml:id"); // lock for a loooong time xml = "<wfs:GetFeatureWithLock" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"1000\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\">" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("cdf/Locks/wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); // issue a lock again, it should be allowed as the lock expired dom = postAsDOM("cdf/Locks/wfs", xml); XMLAssert.assertXpathEvaluatesTo("1", "count(//ows:ExceptionReport)", dom); // release the lock get("cdf/Locks/wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); } @Test public void testLockExpirySeconds() throws Exception { String xml = "<wfs:GetFeature" + " service=\"WFS\"" + " version=\"2.0.0\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\"/>" + "</wfs:GetFeature>"; Document dom = postAsDOM("cdf/Locks/wfs", xml); // get two fids NodeList locks = dom.getElementsByTagName("cdf:Locks"); String fid1 = ((Element) locks.item(0)).getAttribute("gml:id"); String fid2 = ((Element) locks.item(1)).getAttribute("gml:id"); // lock for just one second xml = "<wfs:GetFeatureWithLock" + " service=\"WFS\"" + " version=\"2.0.0\"" + " expiry=\"1\"" + " xmlns:cdf=\"http://www.opengis.net/cite/data\"" + " xmlns:fes='" + FES.NAMESPACE + "' " + " xmlns:wfs='" + WFS.NAMESPACE + "'>" + " <wfs:Query typeNames=\"Locks\">" + " <fes:Filter>" + " <fes:ResourceId rid=\"" + fid1 + "\"/>" + " <fes:ResourceId rid=\"" + fid2 + "\"/>" + " </fes:Filter>" + " </wfs:Query>" + "</wfs:GetFeatureWithLock>"; dom = postAsDOM("cdf/Locks/wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); Thread.sleep(2 * 1000); // issue a lock again, it should be allowed as the lock expired dom = postAsDOM("cdf/Locks/wfs", xml); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); String lockId = dom.getDocumentElement().getAttribute("lockId"); // release the lock get("cdf/Locks/wfs?request=ReleaseLock&version=2.0&lockId=" + lockId); } }