src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java

Tue, 06 Mar 2012 16:09:35 -0800

author
ohair
date
Tue, 06 Mar 2012 16:09:35 -0800
changeset 286
f50545b5e2f1
child 368
0989ad8c0860
permissions
-rw-r--r--

7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom

     1 /*
     2  * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.ws.message.stream;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.istack.internal.Nullable;
    30 import com.sun.istack.internal.XMLStreamReaderToContentHandler;
    31 import com.sun.xml.internal.bind.api.Bridge;
    32 import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
    33 import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator;
    34 import com.sun.xml.internal.ws.api.SOAPVersion;
    35 import com.sun.xml.internal.ws.api.message.AttachmentSet;
    36 import com.sun.xml.internal.ws.api.message.Header;
    37 import com.sun.xml.internal.ws.api.message.HeaderList;
    38 import com.sun.xml.internal.ws.api.message.Message;
    39 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
    40 import com.sun.xml.internal.ws.encoding.TagInfoset;
    41 import com.sun.xml.internal.ws.message.AbstractMessageImpl;
    42 import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl;
    43 import com.sun.xml.internal.ws.spi.db.XMLBridge;
    44 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
    45 import com.sun.xml.internal.ws.util.xml.DummyLocation;
    46 import com.sun.xml.internal.ws.util.xml.StAXSource;
    47 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter;
    48 import org.xml.sax.ContentHandler;
    49 import org.xml.sax.ErrorHandler;
    50 import org.xml.sax.SAXException;
    51 import org.xml.sax.SAXParseException;
    52 import org.xml.sax.helpers.NamespaceSupport;
    54 import javax.xml.bind.JAXBException;
    55 import javax.xml.bind.Unmarshaller;
    56 import javax.xml.stream.*;
    57 import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
    58 import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
    59 import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
    60 import javax.xml.transform.Source;
    61 import javax.xml.ws.WebServiceException;
    62 import java.util.ArrayList;
    63 import java.util.Enumeration;
    64 import java.util.List;
    66 /**
    67  * {@link Message} implementation backed by {@link XMLStreamReader}.
    68  *
    69  * TODO: we need another message class that keeps {@link XMLStreamReader} that points
    70  * at the start of the envelope element.
    71  */
    72 public final class StreamMessage extends AbstractMessageImpl {
    73     /**
    74      * The reader will be positioned at
    75      * the first child of the SOAP body
    76      */
    77     private @NotNull XMLStreamReader reader;
    79     // lazily created
    80     private @Nullable HeaderList headers;
    82     /**
    83      * Because the StreamMessage leaves out the white spaces around payload
    84      * when being instantiated the space characters between soap:Body opening and
    85      * payload is stored in this field to be reused later (necessary for message security);
    86      * Instantiated after StreamMessage creation
    87      */
    88     private String bodyPrologue = null;
    90     /**
    91      * instantiated after writing message to XMLStreamWriter
    92      */
    93     private String bodyEpilogue = null;
    95     private final String payloadLocalName;
    97     private final String payloadNamespaceURI;
    99     /**
   100      * infoset about the SOAP envelope, header, and body.
   101      *
   102      * <p>
   103      * If the creater of this object didn't care about those,
   104      * we use stock values.
   105      */
   106     private @NotNull TagInfoset envelopeTag,headerTag,bodyTag;
   108     /**
   109      * Used only for debugging. This records where the message was consumed.
   110      */
   111     private Throwable consumedAt;
   113     /**
   114      * Default s:Envelope, s:Header, and s:Body tag infoset definitions.
   115      *
   116      * We need 3 for SOAP 1.1, 3 for SOAP 1.2.
   117      */
   118     private static final TagInfoset[] DEFAULT_TAGS;
   120     static {
   121         DEFAULT_TAGS = new TagInfoset[6];
   122         create(SOAPVersion.SOAP_11);
   123         create(SOAPVersion.SOAP_12);
   124     }
   126     /**
   127      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
   128      * that points at the start element of the payload, and headers.
   129      *
   130      * <p>
   131      * This method creaets a {@link Message} from a payload.
   132      *
   133      * @param headers
   134      *      if null, it means no headers. if non-null,
   135      *      it will be owned by this message.
   136      * @param reader
   137      *      points at the start element/document of the payload (or the end element of the &lt;s:Body>
   138      *      if there's no payload)
   139      */
   140     public StreamMessage(@Nullable HeaderList headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   141         super(soapVersion);
   142         this.headers = headers;
   143         this.attachmentSet = attachmentSet;
   144         this.reader = reader;
   146         if(reader.getEventType()== START_DOCUMENT)
   147             XMLStreamReaderUtil.nextElementContent(reader);
   149         //if the reader is pointing to the end element </soapenv:Body> then its empty message
   150         // or no payload
   151         if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   152             String body = reader.getLocalName();
   153             String nsUri = reader.getNamespaceURI();
   154             assert body != null;
   155             assert nsUri != null;
   156             //if its not soapenv:Body then throw exception, we received malformed stream
   157             if(body.equals("Body") && nsUri.equals(soapVersion.nsUri)){
   158                 this.payloadLocalName = null;
   159                 this.payloadNamespaceURI = null;
   160             }else{ //TODO: i18n and also we should be throwing better message that this
   161                 throw new WebServiceException("Malformed stream: {"+nsUri+"}"+body);
   162             }
   163         }else{
   164             this.payloadLocalName = reader.getLocalName();
   165             this.payloadNamespaceURI = reader.getNamespaceURI();
   166         }
   168         // use the default infoset representation for headers
   169         int base = soapVersion.ordinal()*3;
   170         this.envelopeTag = DEFAULT_TAGS[base];
   171         this.headerTag = DEFAULT_TAGS[base+1];
   172         this.bodyTag = DEFAULT_TAGS[base+2];
   173     }
   175     /**
   176      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
   177      * and the complete infoset of the SOAP envelope.
   178      *
   179      * <p>
   180      * See {@link #StreamMessage(HeaderList, AttachmentSet, XMLStreamReader, SOAPVersion)} for
   181      * the description of the basic parameters.
   182      *
   183      * @param headerTag
   184      *      Null if the message didn't have a header tag.
   185      *
   186      */
   187     public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable HeaderList headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   188         this(envelopeTag, headerTag, attachmentSet, headers, null, bodyTag, null, reader, soapVersion);
   189     }
   191     public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable HeaderList headers, @Nullable String bodyPrologue, @NotNull TagInfoset bodyTag, @Nullable String bodyEpilogue, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   192         this(headers,attachmentSet,reader,soapVersion);
   193         if(envelopeTag == null ) {
   194             throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null");
   195         }
   196         if(bodyTag == null ) {
   197             throw new IllegalArgumentException("BodyTag TagInfoset cannot be null");
   198         }
   199         this.envelopeTag = envelopeTag;
   200         this.headerTag = headerTag!=null ? headerTag :
   201             new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
   202         this.bodyTag = bodyTag;
   203         this.bodyPrologue = bodyPrologue;
   204         this.bodyEpilogue = bodyEpilogue;
   205     }
   207     public boolean hasHeaders() {
   208         return headers!=null && !headers.isEmpty();
   209     }
   211     public HeaderList getHeaders() {
   212         if (headers == null) {
   213             headers = new HeaderList();
   214         }
   215         return headers;
   216     }
   218     public String getPayloadLocalPart() {
   219         return payloadLocalName;
   220     }
   222     public String getPayloadNamespaceURI() {
   223         return payloadNamespaceURI;
   224     }
   226     public boolean hasPayload() {
   227         return payloadLocalName!=null;
   228     }
   230     public Source readPayloadAsSource() {
   231         if(hasPayload()) {
   232             assert unconsumed();
   233             return new StAXSource(reader, true, getInscopeNamespaces());
   234         } else
   235             return null;
   236     }
   238     /**
   239      * There is no way to enumerate inscope namespaces for XMLStreamReader. That means
   240      * namespaces declared in envelope, and body tags need to be computed using their
   241      * {@link TagInfoset}s.
   242      *
   243      * @return array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
   244      */
   245     private String[] getInscopeNamespaces() {
   246         NamespaceSupport nss = new NamespaceSupport();
   248         nss.pushContext();
   249         for(int i=0; i < envelopeTag.ns.length; i+=2) {
   250             nss.declarePrefix(envelopeTag.ns[i], envelopeTag.ns[i+1]);
   251         }
   253         nss.pushContext();
   254         for(int i=0; i < bodyTag.ns.length; i+=2) {
   255             nss.declarePrefix(bodyTag.ns[i], bodyTag.ns[i+1]);
   256         }
   258         List<String> inscope = new ArrayList<String>();
   259         for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) {
   260             String prefix = (String)en.nextElement();
   261             inscope.add(prefix);
   262             inscope.add(nss.getURI(prefix));
   263         }
   264         return inscope.toArray(new String[inscope.size()]);
   265     }
   267     public Object readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
   268         if(!hasPayload())
   269             return null;
   270         assert unconsumed();
   271         // TODO: How can the unmarshaller process this as a fragment?
   272         if(hasAttachments())
   273             unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshallerImpl(getAttachments()));
   274         try {
   275             return unmarshaller.unmarshal(reader);
   276         } finally{
   277             unmarshaller.setAttachmentUnmarshaller(null);
   278             XMLStreamReaderUtil.readRest(reader);
   279             XMLStreamReaderUtil.close(reader);
   280             XMLStreamReaderFactory.recycle(reader);
   281         }
   282     }
   283     /** @deprecated */
   284     public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
   285         if(!hasPayload())
   286             return null;
   287         assert unconsumed();
   288         T r = bridge.unmarshal(reader,
   289             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
   290         XMLStreamReaderUtil.readRest(reader);
   291         XMLStreamReaderUtil.close(reader);
   292         XMLStreamReaderFactory.recycle(reader);
   293         return r;
   294     }
   296     public <T> T readPayloadAsJAXB(XMLBridge<T> bridge) throws JAXBException {
   297         if(!hasPayload())
   298             return null;
   299         assert unconsumed();
   300         T r = bridge.unmarshal(reader,
   301             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
   302         XMLStreamReaderUtil.readRest(reader);
   303         XMLStreamReaderUtil.close(reader);
   304         XMLStreamReaderFactory.recycle(reader);
   305         return r;
   306     }
   308     @Override
   309     public void consume() {
   310         assert unconsumed();
   311         XMLStreamReaderUtil.readRest(reader);
   312         XMLStreamReaderUtil.close(reader);
   313         XMLStreamReaderFactory.recycle(reader);
   314     }
   316     public XMLStreamReader readPayload() {
   317         if(!hasPayload())
   318             return null;
   319         // TODO: What about access at and beyond </soap:Body>
   320         assert unconsumed();
   321         return this.reader;
   322     }
   324     public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException {
   325         assert unconsumed();
   327         if(payloadLocalName==null) {
   328             return; // no body
   329         }
   331         if (bodyPrologue != null) {
   332             writer.writeCharacters(bodyPrologue);
   333         }
   335         XMLStreamReaderToXMLStreamWriter conv = new XMLStreamReaderToXMLStreamWriter();
   337         while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   338             String name = reader.getLocalName();
   339             String nsUri = reader.getNamespaceURI();
   341             // After previous conv.bridge() call the cursor will be at END_ELEMENT.
   342             // Check if its not soapenv:Body then move to next ELEMENT
   343             if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   345                 if (!isBodyElement(name, nsUri)){
   346                     // closing payload element: store epilogue for further signing, if applicable
   347                     // however if there more than one payloads exist - the last one is stored
   348                     String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
   349                     if (whiteSpaces != null) {
   350                         this.bodyEpilogue = whiteSpaces;
   351                         // write it to the message too
   352                         writer.writeCharacters(whiteSpaces);
   353                     }
   354                 } else {
   355                     // body closed > exit
   356                     break;
   357                 }
   359             } else {
   360                 // payload opening element: copy payload to writer
   361                 conv.bridge(reader,writer);
   362             }
   363         }
   365         XMLStreamReaderUtil.readRest(reader);
   366         XMLStreamReaderUtil.close(reader);
   367         XMLStreamReaderFactory.recycle(reader);
   368     }
   370     private boolean isBodyElement(String name, String nsUri) {
   371         return name.equals("Body") && nsUri.equals(soapVersion.nsUri);
   372     }
   374     public void writeTo(XMLStreamWriter sw) throws XMLStreamException{
   375         writeEnvelope(sw);
   376     }
   378     /**
   379      * This method should be called when the StreamMessage is created with a payload
   380      * @param writer
   381      */
   382     private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException {
   383         writer.writeStartDocument();
   384         envelopeTag.writeStart(writer);
   386         //write headers
   387         HeaderList hl = getHeaders();
   388         if(hl.size() > 0){
   389             headerTag.writeStart(writer);
   390             for(Header h:hl){
   391                 h.writeTo(writer);
   392             }
   393             writer.writeEndElement();
   394         }
   395         bodyTag.writeStart(writer);
   396         if(hasPayload())
   397             writePayloadTo(writer);
   398         writer.writeEndElement();
   399         writer.writeEndElement();
   400         writer.writeEndDocument();
   401     }
   403     public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
   404         assert unconsumed();
   406         try {
   407             if(payloadLocalName==null)
   408                 return; // no body
   410             if (bodyPrologue != null) {
   411                 char[] chars = bodyPrologue.toCharArray();
   412                 contentHandler.characters(chars, 0, chars.length);
   413             }
   415             XMLStreamReaderToContentHandler conv = new XMLStreamReaderToContentHandler(reader,contentHandler,true,fragment,getInscopeNamespaces());
   417             while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   418                 String name = reader.getLocalName();
   419                 String nsUri = reader.getNamespaceURI();
   421                 // After previous conv.bridge() call the cursor will be at END_ELEMENT.
   422                 // Check if its not soapenv:Body then move to next ELEMENT
   423                 if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   425                     if (!isBodyElement(name, nsUri)){
   426                         // closing payload element: store epilogue for further signing, if applicable
   427                         // however if there more than one payloads exist - the last one is stored
   428                         String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
   429                         if (whiteSpaces != null) {
   430                             this.bodyEpilogue = whiteSpaces;
   431                             // write it to the message too
   432                             char[] chars = whiteSpaces.toCharArray();
   433                             contentHandler.characters(chars, 0, chars.length);
   434                         }
   435                     } else {
   436                         // body closed > exit
   437                         break;
   438                     }
   440                 } else {
   441                     // payload opening element: copy payload to writer
   442                     conv.bridge();
   443                 }
   444             }
   445             XMLStreamReaderUtil.readRest(reader);
   446             XMLStreamReaderUtil.close(reader);
   447             XMLStreamReaderFactory.recycle(reader);
   448         } catch (XMLStreamException e) {
   449             Location loc = e.getLocation();
   450             if(loc==null)   loc = DummyLocation.INSTANCE;
   452             SAXParseException x = new SAXParseException(
   453                 e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e);
   454             errorHandler.error(x);
   455         }
   456     }
   458     // TODO: this method should be probably rewritten to respect spaces between eelements; is it used at all?
   459     public Message copy() {
   460         try {
   461             assert unconsumed();
   462             consumedAt = null; // but we don't want to mark it as consumed
   463             MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
   464             StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb);
   466             // preserving inscope namespaces from envelope, and body. Other option
   467             // would be to create a filtering XMLStreamReader from reader+envelopeTag+bodyTag
   468             c.storeElement(envelopeTag.nsUri, envelopeTag.localName, envelopeTag.prefix, envelopeTag.ns);
   469             c.storeElement(bodyTag.nsUri, bodyTag.localName, bodyTag.prefix, bodyTag.ns);
   471             if (hasPayload()) {
   472                 // Loop all the way for multi payload case
   473                 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   474                     String name = reader.getLocalName();
   475                     String nsUri = reader.getNamespaceURI();
   476                     if(isBodyElement(name, nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
   477                         break;
   478                     c.create(reader);
   480                     // Skip whitespaces in between payload and </Body> or between elements
   481                     // those won't be in the message itself, but we store them in field bodyEpilogue
   482                     if (reader.isWhiteSpace()) {
   483                         bodyEpilogue = XMLStreamReaderUtil.currentWhiteSpaceContent(reader);
   484                     } else {
   485                         // clear it in case the existing was not the last one
   486                         // (we are interested only in the last one?)
   487                         bodyEpilogue = null;
   488                     }
   489                 }
   490             }
   491             c.storeEndElement();        // create structure element for </Body>
   492             c.storeEndElement();        // create structure element for </Envelope>
   493             c.storeEndElement();        // create structure element for END_DOCUMENT
   495             XMLStreamReaderUtil.readRest(reader);
   496             XMLStreamReaderUtil.close(reader);
   497             XMLStreamReaderFactory.recycle(reader);
   499             reader = xsb.readAsXMLStreamReader();
   500             XMLStreamReader clone = xsb.readAsXMLStreamReader();
   502             // advance to the start tag of the <Body> first child element
   503             proceedToRootElement(reader);
   504             proceedToRootElement(clone);
   506             return new StreamMessage(envelopeTag, headerTag, attachmentSet, HeaderList.copy(headers), bodyPrologue, bodyTag, bodyEpilogue, clone, soapVersion);
   507         } catch (XMLStreamException e) {
   508             throw new WebServiceException("Failed to copy a message",e);
   509         }
   510     }
   512     private void proceedToRootElement(XMLStreamReader xsr) throws XMLStreamException {
   513         assert xsr.getEventType()==START_DOCUMENT;
   514         xsr.nextTag();
   515         xsr.nextTag();
   516         xsr.nextTag();
   517         assert xsr.getEventType()==START_ELEMENT || xsr.getEventType()==END_ELEMENT;
   518     }
   520     public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException {
   521         contentHandler.setDocumentLocator(NULL_LOCATOR);
   522         contentHandler.startDocument();
   523         envelopeTag.writeStart(contentHandler);
   524         headerTag.writeStart(contentHandler);
   525         if(hasHeaders()) {
   526             HeaderList headers = getHeaders();
   527             int len = headers.size();
   528             for( int i=0; i<len; i++ ) {
   529                 // shouldn't JDK be smart enough to use array-style indexing for this foreach!?
   530                 headers.get(i).writeTo(contentHandler,errorHandler);
   531             }
   532         }
   533         headerTag.writeEnd(contentHandler);
   534         bodyTag.writeStart(contentHandler);
   535         writePayloadTo(contentHandler,errorHandler, true);
   536         bodyTag.writeEnd(contentHandler);
   537         envelopeTag.writeEnd(contentHandler);
   538         contentHandler.endDocument();
   539     }
   541     /**
   542      * Used for an assertion. Returns true when the message is unconsumed,
   543      * or otherwise throw an exception.
   544      *
   545      * <p>
   546      * Calling this method also marks the stream as 'consumed'
   547      */
   548     private boolean unconsumed() {
   549         if(payloadLocalName==null)
   550             return true;    // no payload. can be consumed multiple times.
   552         if(reader.getEventType()!=XMLStreamReader.START_ELEMENT) {
   553             AssertionError error = new AssertionError("StreamMessage has been already consumed. See the nested exception for where it's consumed");
   554             error.initCause(consumedAt);
   555             throw error;
   556         }
   557         consumedAt = new Exception().fillInStackTrace();
   558         return true;
   559     }
   561     private static void create(SOAPVersion v) {
   562         int base = v.ordinal()*3;
   563         DEFAULT_TAGS[base  ] = new TagInfoset(v.nsUri,"Envelope","S",EMPTY_ATTS,"S",v.nsUri);
   564         DEFAULT_TAGS[base+1] = new TagInfoset(v.nsUri,"Header","S",EMPTY_ATTS);
   565         DEFAULT_TAGS[base+2] = new TagInfoset(v.nsUri,"Body","S",EMPTY_ATTS);
   566     }
   568     public String getBodyPrologue() {
   569         return bodyPrologue;
   570     }
   572     public String getBodyEpilogue() {
   573         return bodyEpilogue;
   574     }
   576 }

mercurial