Modified: incubator/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java (original) +++ incubator/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java Tue May 15 08:52:36 2007 @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; +import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.logging.Level; @@ -36,18 +38,23 @@ import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +//import org.w3c.dom.NodeList; import org.apache.cxf.binding.Binding; +import org.apache.cxf.binding.soap.Soap11; import org.apache.cxf.binding.soap.SoapFault; import org.apache.cxf.binding.soap.SoapMessage; +import org.apache.cxf.binding.soap.SoapVersion; import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.PackageUtils; import org.apache.cxf.endpoint.Endpoint; +import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.BareInInterceptor; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.interceptor.Interceptor; @@ -64,6 +71,7 @@ import org.apache.cxf.service.model.OperationInfo; import org.apache.cxf.ws.addressing.AddressingProperties; import org.apache.cxf.ws.addressing.AttributedURIType; +//import org.apache.cxf.ws.addressing.Names; import org.apache.cxf.ws.addressing.soap.MAPCodec; import org.apache.cxf.ws.rm.AbstractRMInterceptor; import org.apache.cxf.ws.rm.AckRequestedType; @@ -184,12 +192,16 @@ LOG.log(Level.FINE, "encoding RMPs in SOAP headers"); try { - Element header = message.getHeaders(Element.class); + List<Header> header = message.getHeaders(); discardRMHeaders(header); + Document doc = DOMUtils.createDocument(); + SoapVersion version = Soap11.getInstance(); + Element hdr = doc.createElementNS(version.getHeader().getNamespaceURI(), + version.getHeader().getLocalPart()); // add WSRM namespace declaration to header, instead of // repeating in each individual child node - header.setAttributeNS("http://www.w3.org/2000/xmlns/", + hdr.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + RMConstants.getNamespacePrefix(), RMConstants.getNamespace()); Marshaller marshaller = getJAXBContext().createMarshaller(); @@ -200,7 +212,7 @@ encodeProperty(seq, RMConstants.getSequenceQName(), SequenceType.class, - header, + hdr, marshaller); } Collection<SequenceAcknowledgement> acks = rmps.getAcks(); @@ -209,7 +221,7 @@ encodeProperty(ack, RMConstants.getSequenceAckQName(), SequenceAcknowledgement.class, - header, + hdr, marshaller); } } @@ -219,10 +231,15 @@ encodeProperty(ar, RMConstants.getAckRequestedQName(), AckRequestedType.class, - header, + hdr, marshaller); } - } + } + for (int i = 0; i < hdr.getChildNodes().getLength(); i++) { + Node node = hdr.getChildNodes().item(i); + Header holder = new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node); + header.add(holder); + } } catch (SOAPException se) { LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", se); } catch (JAXBException je) { @@ -242,22 +259,34 @@ } LOG.log(Level.FINE, "Encoding SequenceFault in SOAP header"); try { - Element header = message.getHeaders(Element.class); + List<Header> header = message.getHeaders(); discardRMHeaders(header); + Document doc = DOMUtils.createDocument(); + SoapVersion version = message.getVersion(); + Element hdr = doc.createElementNS(version.getHeader().getNamespaceURI(), + version.getHeader().getLocalPart()); // add WSRM namespace declaration to header, instead of // repeating in each individual child node - header.setAttributeNS("http://www.w3.org/2000/xmlns/", - "xmlns:" + RMConstants.getNamespacePrefix(), - RMConstants.getNamespace()); +// hdr.setAttributeNS("http://www.w3.org/2000/xmlns/", +// "xmlns:" + RMConstants.getNamespacePrefix(), +// RMConstants.getNamespace()); Marshaller marshaller = getJAXBContext().createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); encodeProperty(sf.getSequenceFault(), RMConstants.getSequenceFaultQName(), SequenceFaultType.class, - header, + hdr, marshaller); + Node node = hdr.getFirstChild(); + if (node instanceof Element) { + ((Element)node).setAttributeNS("http://www.w3.org/2000/xmlns/", + "xmlns:" + RMConstants.getNamespacePrefix(), + RMConstants.getNamespace()); + } + + header.add(new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node)); } catch (SOAPException se) { LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", se); } catch (JAXBException je) { @@ -290,39 +319,69 @@ Collection<SequenceAcknowledgement> acks = new ArrayList<SequenceAcknowledgement>(); Collection<AckRequestedType> requested = new ArrayList<AckRequestedType>(); - Element header = message.getHeaders(Element.class); + List<Header> header = message.getHeaders(); if (header != null) { Unmarshaller unmarshaller = getJAXBContext().createUnmarshaller(); - NodeList headerElements = header.getChildNodes(); - for (int i = 0; i < headerElements.getLength(); i++) { - Node node = headerElements.item(i); - if (Node.ELEMENT_NODE != node.getNodeType()) { - continue; - } - Element headerElement = (Element)headerElements.item(i); - String headerURI = headerElement.getNamespaceURI(); - String localName = headerElement.getLocalName(); - if (RMConstants.getNamespace().equals(headerURI)) { - LOG.log(Level.FINE, "decoding RM header {0}", localName); - if (RMConstants.getSequenceName().equals(localName)) { - SequenceType s = decodeProperty(SequenceType.class, - headerElement, - unmarshaller); - - rmps.setSequence(s); - } else if (RMConstants.getSequenceAckName().equals(localName)) { - SequenceAcknowledgement ack = decodeProperty(SequenceAcknowledgement.class, - headerElement, - unmarshaller); - acks.add(ack); - } else if (RMConstants.getAckRequestedName().equals(localName)) { - AckRequestedType ar = decodeProperty(AckRequestedType.class, - headerElement, - unmarshaller); - requested.add(ar); +// NodeList headerElements = header.getChildNodes(); + Iterator<Header> iter = header.iterator(); + while (iter.hasNext()) { + Object node = iter.next().getObject(); + if (node instanceof Element) { + Element elem = (Element) node; + if (Node.ELEMENT_NODE != elem.getNodeType()) { + continue; + } + String headerURI = elem.getNamespaceURI(); + String localName = elem.getLocalName(); + if (RMConstants.getNamespace().equals(headerURI)) { + LOG.log(Level.FINE, "decoding RM header {0}", localName); + if (RMConstants.getSequenceName().equals(localName)) { + SequenceType s = decodeProperty(SequenceType.class, + elem, + unmarshaller); + + rmps.setSequence(s); + } else if (RMConstants.getSequenceAckName().equals(localName)) { + SequenceAcknowledgement ack = decodeProperty(SequenceAcknowledgement.class, + elem, + unmarshaller); + acks.add(ack); + } else if (RMConstants.getAckRequestedName().equals(localName)) { + AckRequestedType ar = decodeProperty(AckRequestedType.class, + elem, + unmarshaller); + requested.add(ar); + } } +// for (int i = 0; i < headerElements.getLength(); i++) { +// Node node = headerElements.item(i); +// if (Node.ELEMENT_NODE != node.getNodeType()) { +// continue; +// } +// Element headerElement = (Element)headerElements.item(i); +// String headerURI = headerElement.getNamespaceURI(); +// String localName = headerElement.getLocalName(); +// if (RMConstants.getNamespace().equals(headerURI)) { +// LOG.log(Level.FINE, "decoding RM header {0}", localName); +// if (RMConstants.getSequenceName().equals(localName)) { +// SequenceType s = decodeProperty(SequenceType.class, +// headerElement, +// unmarshaller); +// +// rmps.setSequence(s); +// } else if (RMConstants.getSequenceAckName().equals(localName)) { +// SequenceAcknowledgement ack = decodeProperty(SequenceAcknowledgement.class, +// headerElement, +// unmarshaller); +// acks.add(ack); +// } else if (RMConstants.getAckRequestedName().equals(localName)) { +// AckRequestedType ar = decodeProperty(AckRequestedType.class, +// headerElement, +// unmarshaller); +// requested.add(ar); +// } } } if (acks.size() > 0) { @@ -397,15 +456,24 @@ * * @param header the SOAP header element */ - private static void discardRMHeaders(Element header) throws SOAPException { - NodeList headerElements = - header.getElementsByTagNameNS(RMConstants.getNamespace(), "*"); - for (int i = 0; i < headerElements.getLength(); i++) { - Node headerElement = headerElements.item(i); - if (RMConstants.getNamespace().equals(headerElement.getNamespaceURI())) { - header.removeChild(headerElement); + private static void discardRMHeaders(List<Header> header) throws SOAPException { + + Iterator<Header> iter = header.iterator(); + while (iter.hasNext()) { + Header hdr = iter.next(); + if (RMConstants.getNamespace().equals(hdr.getName().getNamespaceURI())) { + iter.remove(); } } + +// NodeList headerElements = +// header.getElementsByTagNameNS(RMConstants.getNamespace(), "*"); +// for (int i = 0; i < headerElements.getLength(); i++) { +// Node headerElement = headerElements.item(i); +// if (RMConstants.getNamespace().equals(headerElement.getNamespaceURI())) { +// header.removeChild(headerElement); +// } +// } } @@ -534,6 +602,7 @@ return null; } } +
Modified: incubator/cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptorTest.java URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptorTest.java?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptorTest.java (original) +++ incubator/cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptorTest.java Tue May 15 08:52:36 2007 @@ -24,6 +24,8 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; +import java.util.List; import java.util.Set; import javax.xml.namespace.QName; @@ -32,11 +34,13 @@ import javax.xml.stream.XMLStreamReader; import org.w3c.dom.Element; -import org.w3c.dom.NodeList; +//import org.w3c.dom.NodeList; +import org.apache.cxf.BusFactory; import org.apache.cxf.binding.soap.SoapFault; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor; +import org.apache.cxf.headers.Header; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; @@ -349,48 +353,85 @@ } private void verifyHeaders(SoapMessage message, String... names) { - Element header = message.getHeaders(Element.class); + List<Header> header = message.getHeaders(); // check all expected headers are present for (String name : names) { boolean found = false; - NodeList headerElements = header.getChildNodes(); - for (int i = 0; i < headerElements.getLength(); i++) { - Element headerElement = (Element)headerElements.item(i); - String namespace = headerElement.getNamespaceURI(); - String localName = headerElement.getLocalName(); - if (RMConstants.getNamespace().equals(namespace) - && localName.equals(name)) { - found = true; - break; - } else if (RMConstants.getAddressingNamespace().equals(namespace) - && localName.equals(name)) { - found = true; - break; +// NodeList headerElements = header.getChildNodes(); + Iterator<Header> iter = header.iterator(); + while (iter.hasNext()) { + Object obj = iter.next().getObject(); + if (obj instanceof Element) { + Element elem = (Element) obj; + String namespace = elem.getNamespaceURI(); + String localName = elem.getLocalName(); + if (RMConstants.getNamespace().equals(namespace) + && localName.equals(name)) { + found = true; + break; + } else if (RMConstants.getAddressingNamespace().equals(namespace) + && localName.equals(name)) { + found = true; + break; + } } } +// for (int i = 0; i < headerElements.getLength(); i++) { +// Element headerElement = (Element)headerElements.item(i); +// String namespace = headerElement.getNamespaceURI(); +// String localName = headerElement.getLocalName(); +// if (RMConstants.getNamespace().equals(namespace) +// && localName.equals(name)) { +// found = true; +// break; +// } else if (RMConstants.getAddressingNamespace().equals(namespace) +// && localName.equals(name)) { +// found = true; +// break; +// } +// } assertTrue("Could not find header element " + name, found); } // no other headers should be present - NodeList headerElements = header.getChildNodes(); - for (int i = 0; i < headerElements.getLength(); i++) { - Element headerElement = (Element)headerElements.item(i); - String namespace = headerElement.getNamespaceURI(); - String localName = headerElement.getLocalName(); - assertTrue(RMConstants.getNamespace().equals(namespace) - || RMConstants.getAddressingNamespace().equals(namespace)); - boolean found = false; - for (String name : names) { - if (localName.equals(name)) { - found = true; - break; + Iterator<Header> iter1 = header.iterator(); + while (iter1.hasNext()) { + Object obj = iter1.next().getObject(); + if (obj instanceof Element) { + Element elem = (Element) obj; + String namespace = elem.getNamespaceURI(); + String localName = elem.getLocalName(); + assertTrue(RMConstants.getNamespace().equals(namespace) + || RMConstants.getAddressingNamespace().equals(namespace)); + boolean found = false; + for (String name : names) { + if (localName.equals(name)) { + found = true; + break; + } } + assertTrue("Unexpected header element " + localName, found); } - assertTrue("Unexpected header element " + localName, found); } +// NodeList headerElements = header.getChildNodes(); +// for (int i = 0; i < headerElements.getLength(); i++) { +// Element headerElement = (Element)headerElements.item(i); +// String namespace = headerElement.getNamespaceURI(); +// String localName = headerElement.getLocalName(); +// assertTrue(RMConstants.getNamespace().equals(namespace) +// || RMConstants.getAddressingNamespace().equals(namespace)); +// boolean found = false; +// for (String name : names) { +// if (localName.equals(name)) { +// found = true; +// break; +// } +// } +// assertTrue("Unexpected header element " + localName, found); +// } } private SoapMessage setUpInboundMessage(String resource) throws XMLStreamException { @@ -400,7 +441,7 @@ assertNotNull(is); XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is); soapMessage.setContent(XMLStreamReader.class, reader); - ReadHeadersInterceptor rji = new ReadHeadersInterceptor(); + ReadHeadersInterceptor rji = new ReadHeadersInterceptor(BusFactory.getDefaultBus()); rji.handleMessage(soapMessage); return soapMessage; } Modified: incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/HeaderVerifier.java URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/HeaderVerifier.java?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/HeaderVerifier.java (original) +++ incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/HeaderVerifier.java Tue May 15 08:52:36 2007 @@ -21,6 +21,7 @@ import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -31,12 +32,15 @@ import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; +import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.apache.cxf.binding.soap.SoapMessage; +import org.apache.cxf.binding.soap.SoapVersion; import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; +import org.apache.cxf.headers.Header; +import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.phase.Phase; import org.apache.cxf.ws.addressing.AddressingProperties; import org.apache.cxf.ws.addressing.AttributedURIType; @@ -84,8 +88,27 @@ private void addPartialResponseHeader(SoapMessage message) { try { // add piggybacked wsa:From header to partial response - Element header = message.getHeaders(Element.class); - marshallFrom("urn:piggyback_responder", header, getMarshaller()); +// Element header = message.getHeaders(Element.class); + List<Header> header = message.getHeaders(); + Document doc = DOMUtils.createDocument(); + SoapVersion ver = message.getVersion(); + Element hdr = doc.createElementNS(ver.getHeader().getNamespaceURI(), + ver.getHeader().getLocalPart()); + hdr.setPrefix(ver.getHeader().getPrefix()); + + marshallFrom("urn:piggyback_responder", hdr, getMarshaller()); + NodeList nl = hdr.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Object obj = nl.item(i); + if (obj instanceof Element) { + Element elem = (Element) obj; + Header holder = new Header( + new QName(elem.getNamespaceURI(), elem.getLocalName()), + elem, null); + header.add(holder); + } + } + } catch (Exception e) { verificationCache.put("SOAP header addition failed: " + e); e.printStackTrace(); @@ -95,7 +118,7 @@ private void verify(SoapMessage message, boolean outgoingPartialResponse) { try { List<String> wsaHeaders = new ArrayList<String>(); - Element headers = message.getHeaders(Element.class); + List<Header> headers = message.getHeaders(); if (headers != null) { recordWSAHeaders(headers, wsaHeaders, @@ -113,18 +136,31 @@ } } - private void recordWSAHeaders(Element headers, + private void recordWSAHeaders(List<Header> headers, List<String> wsaHeaders, String namespaceURI) { - NodeList headerElements = - headers.getElementsByTagNameNS(namespaceURI, "*"); - for (int i = 0; i < headerElements.getLength(); i++) { - Node headerElement = headerElements.item(i); - if (namespaceURI.equals(headerElement.getNamespaceURI())) { - currentNamespaceURI = namespaceURI; - wsaHeaders.add(headerElement.getLocalName()); + Iterator<Header> iter = headers.iterator(); + while (iter.hasNext()) { + Object obj = iter.next().getObject(); + if (obj instanceof Element) { + Element hdr = (Element) obj; + if (namespaceURI.equals(hdr.getNamespaceURI())) { + currentNamespaceURI = namespaceURI; + wsaHeaders.add(hdr.getLocalName()); + } } + } + +// NodeList headerElements = +// headers.getElementsByTagNameNS(namespaceURI, "*"); +// for (int i = 0; i < headerElements.getLength(); i++) { +// Node headerElement = headerElements.item(i); +// if (namespaceURI.equals(headerElement.getNamespaceURI())) { +// currentNamespaceURI = namespaceURI; +// wsaHeaders.add(headerElement.getLocalName()); +// } +// } } private boolean isOutgoingPartialResponse(SoapMessage message) { Modified: incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/MAPTestBase.java URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/MAPTestBase.java?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/MAPTestBase.java (original) +++ incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/MAPTestBase.java Tue May 15 08:52:36 2007 @@ -395,3 +395,4 @@ } } + Modified: incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/Server.java URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/Server.java?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/Server.java (original) +++ incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/Server.java Tue May 15 08:52:36 2007 @@ -39,7 +39,7 @@ protected void run() { SpringBusFactory factory = new SpringBusFactory(); - Bus bus = factory.createBus("org/apache/cxf/systest/ws/addressing/cxf.xml"); + Bus bus = factory.createBus("org/apache/cxf/systest/ws/addressing/wsa_interceptors.xml"); BusFactory.setDefaultBus(bus); setBus(bus); Modified: incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/cxf.xml URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/cxf.xml?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/cxf.xml (original) +++ incubator/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/ws/addressing/cxf.xml Tue May 15 08:52:36 2007 @@ -30,3 +30,4 @@ <import resource="wsa_interceptors.xml"/> </beans> + Modified: incubator/cxf/trunk/testutils/pom.xml URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/testutils/pom.xml?view=diff&rev=538231&r1=538230&r2=538231 ============================================================================== --- incubator/cxf/trunk/testutils/pom.xml (original) +++ incubator/cxf/trunk/testutils/pom.xml Tue May 15 08:52:36 2007 @@ -341,6 +341,44 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-common-xsd</artifactId> + <version>${version}</version> + <executions> + <execution> + <id>generate-sources</id> + <phase>generate-sources</phase> + <configuration> + <sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot> + <xsdOptions> + <xsdOption> + <xsd>${basedir}/src/main/resources/wsdl/oob_headertype.xsd</xsd> + <extension>true</extension> + <extensionArgs> + <extensionArg>-Xdv</extensionArg> + </extensionArgs> + </xsdOption> + </xsdOptions> + </configuration> + <goals> + <goal>xsdtojava</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-xjc-dv</artifactId> + <version>${version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-common-utilities</artifactId> + <version>${version}</version> + </dependency> + </dependencies> + </plugin> </plugins> </build> @@ -351,4 +389,4 @@ <url>http://svn.apache.org/viewvc/incubator/cxf/trunk/cxf-parent/cxf-testutils</url> </scm> -</project> \ No newline at end of file +</project>
