Enhancements on Override for public ids during geonetwork harvesting
Updated pom version at 1.8.0 git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/data-transfer/uri-resolver@130913 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
parent
44eeedec96
commit
406af9e14d
2
pom.xml
2
pom.xml
|
@ -8,7 +8,7 @@
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.gcube.data.transfer</groupId>
|
<groupId>org.gcube.data.transfer</groupId>
|
||||||
<artifactId>uri-resolver</artifactId>
|
<artifactId>uri-resolver</artifactId>
|
||||||
<version>1.7.0-SNAPSHOT</version>
|
<version>1.8.0-SNAPSHOT</version>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
<description>The URI Resolver is an HTTP URI resolver implemented as an HTTP servlet which gives access trough HTTP to different protocols URIs. </description>
|
<description>The URI Resolver is an HTTP URI resolver implemented as an HTTP servlet which gives access trough HTTP to different protocols URIs. </description>
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ package org.gcube.datatransfer.resolver;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The Class GeonetworkRequestCriteria.
|
||||||
*
|
*
|
||||||
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||||
* Jun 15, 2016
|
* Jun 15, 2016
|
||||||
|
@ -12,7 +13,6 @@ package org.gcube.datatransfer.resolver;
|
||||||
public class GeonetworkRequestCriteria {
|
public class GeonetworkRequestCriteria {
|
||||||
|
|
||||||
private String scope;
|
private String scope;
|
||||||
private final String FILTER_PUBLIC_IDS = "FILTER_PUBLIC_IDS";
|
|
||||||
private boolean valueOfFilterPublicIds;
|
private boolean valueOfFilterPublicIds;
|
||||||
private boolean authOnGeonetwork;
|
private boolean authOnGeonetwork;
|
||||||
private boolean noAuthOnGeonetwork;
|
private boolean noAuthOnGeonetwork;
|
||||||
|
@ -24,15 +24,16 @@ public class GeonetworkRequestCriteria {
|
||||||
* @param valueOfFilterPublicIds the value of filter public ids
|
* @param valueOfFilterPublicIds the value of filter public ids
|
||||||
* @param noAuthOnGeonetowrk the no auth on geonetowrk
|
* @param noAuthOnGeonetowrk the no auth on geonetowrk
|
||||||
*/
|
*/
|
||||||
GeonetworkRequestCriteria(String scope, boolean valueOfFilterPublicIds, boolean noAuthOnGeonetowrk){
|
public GeonetworkRequestCriteria(String scope, boolean valueOfFilterPublicIds, boolean noAuthOnGeonetowrk){
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.valueOfFilterPublicIds = valueOfFilterPublicIds;
|
this.valueOfFilterPublicIds = valueOfFilterPublicIds;
|
||||||
this.noAuthOnGeonetwork = noAuthOnGeonetowrk;
|
this.noAuthOnGeonetwork = noAuthOnGeonetowrk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Checks if is no auth on geonetwork.
|
||||||
|
*
|
||||||
* @return the noAuthOnGeonetwork
|
* @return the noAuthOnGeonetwork
|
||||||
*/
|
*/
|
||||||
public boolean isNoAuthOnGeonetwork() {
|
public boolean isNoAuthOnGeonetwork() {
|
||||||
|
@ -42,6 +43,8 @@ public class GeonetworkRequestCriteria {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the scope.
|
||||||
|
*
|
||||||
* @return the scope
|
* @return the scope
|
||||||
*/
|
*/
|
||||||
public String getScope() {
|
public String getScope() {
|
||||||
|
@ -51,6 +54,8 @@ public class GeonetworkRequestCriteria {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Checks if is value of filter public ids.
|
||||||
|
*
|
||||||
* @return the valueOfFilterPublicIds
|
* @return the valueOfFilterPublicIds
|
||||||
*/
|
*/
|
||||||
public boolean isValueOfFilterPublicIds() {
|
public boolean isValueOfFilterPublicIds() {
|
||||||
|
@ -60,6 +65,8 @@ public class GeonetworkRequestCriteria {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Sets the scope.
|
||||||
|
*
|
||||||
* @param scope the scope to set
|
* @param scope the scope to set
|
||||||
*/
|
*/
|
||||||
public void setScope(String scope) {
|
public void setScope(String scope) {
|
||||||
|
@ -69,6 +76,8 @@ public class GeonetworkRequestCriteria {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Sets the value of filter public ids.
|
||||||
|
*
|
||||||
* @param valueOfFilterPublicIds the valueOfFilterPublicIds to set
|
* @param valueOfFilterPublicIds the valueOfFilterPublicIds to set
|
||||||
*/
|
*/
|
||||||
public void setValueOfFilterPublicIds(boolean valueOfFilterPublicIds) {
|
public void setValueOfFilterPublicIds(boolean valueOfFilterPublicIds) {
|
||||||
|
@ -87,12 +96,12 @@ public class GeonetworkRequestCriteria {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append("GeonetworkRequestCriteria [scope=");
|
builder.append("GeonetworkRequestCriteria [scope=");
|
||||||
builder.append(scope);
|
builder.append(scope);
|
||||||
builder.append(", FILTER_PUBLIC_IDS=");
|
|
||||||
builder.append(FILTER_PUBLIC_IDS);
|
|
||||||
builder.append(", valueOfFilterPublicIds=");
|
builder.append(", valueOfFilterPublicIds=");
|
||||||
builder.append(valueOfFilterPublicIds);
|
builder.append(valueOfFilterPublicIds);
|
||||||
builder.append(", authOnGeonetwork=");
|
builder.append(", authOnGeonetwork=");
|
||||||
builder.append(authOnGeonetwork);
|
builder.append(authOnGeonetwork);
|
||||||
|
builder.append(", noAuthOnGeonetwork=");
|
||||||
|
builder.append(noAuthOnGeonetwork);
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,18 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class GeonetworkScopeDecoder.
|
* The Class GeonetworkRequestDecoder.
|
||||||
*
|
*
|
||||||
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||||
* Gets the new uri to forward the request to {@link GeonetworkResolver}
|
* Jul 27, 2016
|
||||||
* Jun 15, 2016
|
*
|
||||||
|
* This class parses a request from servletpath and queryString
|
||||||
|
*
|
||||||
|
* The request must be: SCOPE#PARMETERS
|
||||||
|
* SCOPE must be: separated by _
|
||||||
|
* PARAMETERS can be:
|
||||||
|
* {@link GeonetworkResolver#PARAMETER_FILTER_PUBLIC_IDS}
|
||||||
|
* {@link GeonetworkResolver#PARAMETER_NO_AUTHENTICATION}
|
||||||
*/
|
*/
|
||||||
public class GeonetworkRequestDecoder {
|
public class GeonetworkRequestDecoder {
|
||||||
|
|
||||||
|
@ -24,6 +31,8 @@ public class GeonetworkRequestDecoder {
|
||||||
|
|
||||||
private String newURI;
|
private String newURI;
|
||||||
|
|
||||||
|
private GeonetworkRequestCriteria geonetworkRequestCriteria;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new geonetwork request decoder.
|
* Instantiates a new geonetwork request decoder.
|
||||||
*
|
*
|
||||||
|
@ -41,11 +50,11 @@ public class GeonetworkRequestDecoder {
|
||||||
throw new ServletException("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
throw new ServletException("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
||||||
}
|
}
|
||||||
|
|
||||||
GeonetworkRequestCriteria rc = getGeonetworkRequestCriteria(params[0]);
|
geonetworkRequestCriteria = getGeonetworkRequestCriteria(params[0]);
|
||||||
logger.debug("scope value is: "+rc.getScope());
|
logger.debug("scope value is: "+geonetworkRequestCriteria.getScope());
|
||||||
newURI = UriResolverRewriteFilter.SERVLET_GEONETWORK + "?" + GeonetworkResolver.SCOPE + "=" + rc.getScope() +"&"+ GeonetworkResolver.PARAMETER_FILTER_PUBLIC_IDS +"="+rc.isValueOfFilterPublicIds() +"&"+GeonetworkResolver.PARAMETER_NO_AUTHENTICATION+"="+rc.isNoAuthOnGeonetwork();
|
newURI = UriResolverRewriteFilter.SERVLET_GEONETWORK + "?" + GeonetworkResolver.SCOPE + "=" + geonetworkRequestCriteria.getScope() +"&"+ GeonetworkResolver.PARAMETER_FILTER_PUBLIC_IDS +"="+geonetworkRequestCriteria.isValueOfFilterPublicIds() +"&"+GeonetworkResolver.PARAMETER_NO_AUTHENTICATION+"="+geonetworkRequestCriteria.isNoAuthOnGeonetwork();
|
||||||
logger.debug(GeonetworkResolver.PARAMETER_FILTER_PUBLIC_IDS +" is: "+rc.isValueOfFilterPublicIds());
|
logger.debug(GeonetworkResolver.PARAMETER_FILTER_PUBLIC_IDS +" is: "+geonetworkRequestCriteria.isValueOfFilterPublicIds());
|
||||||
logger.debug(GeonetworkResolver.PARAMETER_NO_AUTHENTICATION +" is: "+rc.isNoAuthOnGeonetwork());
|
logger.debug(GeonetworkResolver.PARAMETER_NO_AUTHENTICATION +" is: "+geonetworkRequestCriteria.isNoAuthOnGeonetwork());
|
||||||
|
|
||||||
if(params.length>1){
|
if(params.length>1){
|
||||||
String remainPath = "";
|
String remainPath = "";
|
||||||
|
@ -63,7 +72,6 @@ public class GeonetworkRequestDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the geonetwork request criteria.
|
* Gets the geonetwork request criteria.
|
||||||
* Parses a request like root_vo_vre#filterPublicIds or root_vo_vre
|
* Parses a request like root_vo_vre#filterPublicIds or root_vo_vre
|
||||||
|
@ -99,9 +107,28 @@ public class GeonetworkRequestDecoder {
|
||||||
return newURI;
|
return newURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
/**
|
||||||
|
* @return the geonetworkRequestCriteria
|
||||||
|
*/
|
||||||
|
public GeonetworkRequestCriteria getGeonetworkRequestCriteria() {
|
||||||
|
|
||||||
System.out.println(getGeonetworkRequestCriteria("gcube_devsec_devVRE#filterpublicids"));
|
return geonetworkRequestCriteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("GeonetworkRequestDecoder [newURI=");
|
||||||
|
builder.append(newURI);
|
||||||
|
builder.append(", geonetworkRequestCriteria=");
|
||||||
|
builder.append(geonetworkRequestCriteria);
|
||||||
|
builder.append("]");
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,43 +126,4 @@ public class UriResolverRewriteFilter implements Filter{
|
||||||
logger.trace("run init");
|
logger.trace("run init");
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public static void main(String[] args) throws ServletException {
|
|
||||||
|
|
||||||
String path = "/geonetwork/gcube_devsec_devVRE/srv/en/mef.export";
|
|
||||||
String queryString = "p1=1&p2=2";
|
|
||||||
String pathWithoutGN = path.substring(SERVLET_GEONETWORK.length()+1, path.length());
|
|
||||||
logger.debug("servlet path without "+SERVLET_GEONETWORK + " is:" +pathWithoutGN);
|
|
||||||
String[] params = pathWithoutGN.split("/");
|
|
||||||
|
|
||||||
System.out.println(Arrays.asList(params));
|
|
||||||
|
|
||||||
if(params[0]==null || params[0].isEmpty()){
|
|
||||||
logger.error("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
|
||||||
throw new ServletException("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
|
||||||
}
|
|
||||||
|
|
||||||
String scopeValue = getScope(params[0]);
|
|
||||||
logger.debug("scope value is: "+scopeValue);
|
|
||||||
String newURI = SERVLET_GEONETWORK + "?" + GeonetworkResolver.SCOPE + "=" + scopeValue;
|
|
||||||
|
|
||||||
if(params.length>1){
|
|
||||||
String remainPath = "";
|
|
||||||
// newURI +="&remainPath=";
|
|
||||||
for (int i = 1; i < params.length; i++) {
|
|
||||||
String httpGetParam = params[i];
|
|
||||||
if(httpGetParam!=null && !httpGetParam.isEmpty())
|
|
||||||
remainPath+="/"+httpGetParam;
|
|
||||||
}
|
|
||||||
|
|
||||||
newURI +="&"+GeonetworkResolver.REMAIN_PATH+"="+remainPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(queryString!=null && !queryString.isEmpty())
|
|
||||||
newURI+="&"+queryString;
|
|
||||||
|
|
||||||
logger.debug("forward "+newURI);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,7 +338,7 @@ public class GeonetworkResolver extends HttpServlet{
|
||||||
logger.info("Sending CSW POST request to URL: "+gnCSWlURL);
|
logger.info("Sending CSW POST request to URL: "+gnCSWlURL);
|
||||||
logger.info("Content-Type: "+req.getContentType());
|
logger.info("Content-Type: "+req.getContentType());
|
||||||
//DEBUG
|
//DEBUG
|
||||||
logger.trace("POST - BODY : "+byteArray.toString());
|
//logger.debug("POST - BODY : "+byteArray.toString());
|
||||||
InputStream in = httpUtils.post(gnCSWlURL, new ByteArrayInputStream(byteArray.toByteArray()), req.getContentType(), req.getParameterMap());
|
InputStream in = httpUtils.post(gnCSWlURL, new ByteArrayInputStream(byteArray.toByteArray()), req.getContentType(), req.getParameterMap());
|
||||||
//END DEBUG
|
//END DEBUG
|
||||||
logger.info("Response return Content-Type: "+httpUtils.getLastContentType());
|
logger.info("Response return Content-Type: "+httpUtils.getLastContentType());
|
||||||
|
|
|
@ -84,12 +84,26 @@ public class GetResponseRecordFilter {
|
||||||
logger.debug("gmd:MD_Metadata are: " + nodes.getLength());
|
logger.debug("gmd:MD_Metadata are: " + nodes.getLength());
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
Element mdMetadata = (Element) nodes.item(i);
|
Element mdMetadata = (Element) nodes.item(i);
|
||||||
|
|
||||||
// <dc:identifier>
|
// <dc:identifier>
|
||||||
Element id = (Element) mdMetadata.getElementsByTagName("gmd:fileIdentifier").item(0);
|
NodeList fileIdentifierLst = mdMetadata.getElementsByTagName("gmd:fileIdentifier");
|
||||||
Element gco = (Element) id.getElementsByTagName("gco:CharacterString").item(0);
|
if(fileIdentifierLst==null || fileIdentifierLst.getLength()==0 || fileIdentifierLst.item(0)==null){
|
||||||
|
logger.info("skipping identifier: " + identifier +" it has not fileidentifier");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element id = (Element) fileIdentifierLst.item(0);
|
||||||
|
|
||||||
|
NodeList gcoLst = id.getElementsByTagName("gco:CharacterString");
|
||||||
|
if(gcoLst==null || gcoLst.getLength()==0 || gcoLst.item(0)==null){
|
||||||
|
logger.info("skipping identifier: " + identifier +" it has not gco:CharacterString");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element gco = (Element) gcoLst.item(0);
|
||||||
String idValue = gco.getTextContent();
|
String idValue = gco.getTextContent();
|
||||||
logger.trace("Summary gmd:fileIdentifier is: " + idValue);
|
logger.trace("Summary gmd:fileIdentifier is: " + idValue);
|
||||||
if (idValue.equals(identifier)) {
|
if (idValue!=null && idValue.equals(identifier)) {
|
||||||
gco.setTextContent(A_PUBLIC_ID_PLEASE_IGNORE);
|
gco.setTextContent(A_PUBLIC_ID_PLEASE_IGNORE);
|
||||||
logger.debug("Overrided child " + idValue);
|
logger.debug("Overrided child " + idValue);
|
||||||
return true;
|
return true;
|
||||||
|
@ -121,7 +135,7 @@ public class GetResponseRecordFilter {
|
||||||
if(overrideSummaryRecord(doc, identifier))
|
if(overrideSummaryRecord(doc, identifier))
|
||||||
override++;
|
override++;
|
||||||
}
|
}
|
||||||
logger.debug("Overrided "+override +" node/s");
|
logger.info("Overrided "+override +" node/s");
|
||||||
|
|
||||||
//TODO IS IT POSSIBLE TO REMOVE?
|
//TODO IS IT POSSIBLE TO REMOVE?
|
||||||
/*NodeList nodeList = doc.getElementsByTagName("csw:SearchResults");
|
/*NodeList nodeList = doc.getElementsByTagName("csw:SearchResults");
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||||
|
* Jul 29, 2016
|
||||||
|
*/
|
||||||
|
public class GeonetworkMefServiceTest {
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static void main(String[] args) throws ServletException {
|
||||||
|
|
||||||
|
String path = "/geonetwork/gcube_devsec_devVRE/srv/en/mef.export";
|
||||||
|
String queryString = "p1=1&p2=2";
|
||||||
|
String pathWithoutGN = path.substring(SERVLET_GEONETWORK.length()+1, path.length());
|
||||||
|
logger.debug("servlet path without "+SERVLET_GEONETWORK + " is:" +pathWithoutGN);
|
||||||
|
String[] params = pathWithoutGN.split("/");
|
||||||
|
|
||||||
|
System.out.println(Arrays.asList(params));
|
||||||
|
|
||||||
|
if(params[0]==null || params[0].isEmpty()){
|
||||||
|
logger.error("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
||||||
|
throw new ServletException("Scope is null or empty, you must set a valid scope /geonetwork/root_vo_vre");
|
||||||
|
}
|
||||||
|
|
||||||
|
String scopeValue = getScope(params[0]);
|
||||||
|
logger.debug("scope value is: "+scopeValue);
|
||||||
|
String newURI = SERVLET_GEONETWORK + "?" + GeonetworkResolver.SCOPE + "=" + scopeValue;
|
||||||
|
|
||||||
|
if(params.length>1){
|
||||||
|
String remainPath = "";
|
||||||
|
// newURI +="&remainPath=";
|
||||||
|
for (int i = 1; i < params.length; i++) {
|
||||||
|
String httpGetParam = params[i];
|
||||||
|
if(httpGetParam!=null && !httpGetParam.isEmpty())
|
||||||
|
remainPath+="/"+httpGetParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
newURI +="&"+GeonetworkResolver.REMAIN_PATH+"="+remainPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(queryString!=null && !queryString.isEmpty())
|
||||||
|
newURI+="&"+queryString;
|
||||||
|
|
||||||
|
logger.debug("forward "+newURI);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
import org.gcube.datatransfer.resolver.GeonetworkRequestDecoder;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||||
|
* Jul 27, 2016
|
||||||
|
*/
|
||||||
|
public class GeonetworkRequestDecoderTest {
|
||||||
|
|
||||||
|
|
||||||
|
//@Test
|
||||||
|
public void test1() throws ServletException{
|
||||||
|
String request = "/geonetwork/gcube_devsec_devVRE#filterpublicids";
|
||||||
|
System.out.println("Testing request: "+request);
|
||||||
|
GeonetworkRequestDecoder gd = new GeonetworkRequestDecoder(request,"");
|
||||||
|
System.out.println(gd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@Test
|
||||||
|
public void test2() throws ServletException{
|
||||||
|
String request = "/geonetwork/gcube_devsec_devVRE#noauthentication";
|
||||||
|
System.out.println("Testing request: "+request);
|
||||||
|
GeonetworkRequestDecoder gd = new GeonetworkRequestDecoder(request,"");
|
||||||
|
System.out.println(gd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test3() throws ServletException{
|
||||||
|
String request = "/gcube_devsec";
|
||||||
|
System.out.println("Testing request: "+request);
|
||||||
|
GeonetworkRequestDecoder gd = new GeonetworkRequestDecoder(request,"");
|
||||||
|
System.out.println(gd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue