/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.core.common.chr.dcr;
import com.ericsson.ssa.sip.TelURLImpl;
import com.ericsson.ssa.sip.UriUtil;
import com.ericsson.ssa.sip.UriUtil.UriUtilException;
import org.glassfish.comms.api.telurl.TelUrlResolver;
import org.glassfish.comms.api.telurl.TelUrlResolverException;
import org.glassfish.comms.api.uriutils.UriTools;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TelURL;
import javax.servlet.sip.URI;
/**
* Utilities for the dynamic data centric rules.
*/
public class DcrUtils {
private static TelUrlResolver resolver;
private static SipFactory sf;
private static DcrUtils instance;
private static Logger logger = LogUtil.CLB_LOGGER.getLogger();
private static UriTools uriUtil;
private DcrUtils() {
}
public static void setup(TelUrlResolver resolver, SipFactory sf, UriTools uriUtil) {
DcrUtils.resolver = resolver;
DcrUtils.sf = sf;
DcrUtils.uriUtil = uriUtil;
}
public static DcrUtils getInstance() {
if ((resolver == null) || (sf == null)) {
throw new IllegalStateException("The DcrUtils has not been setup!");
}
if (instance == null) {
instance = new DcrUtils();
}
return instance;
}
/**
* A forgiving variant of createURI. If the given string cannot be parsed as
* a URI, try to parse it as an Address instead and extract the URI from
* there.
*
*/
private URI forgivingCreateURI(String x) throws ServletParseException {
try {
URI u = sf.createURI(x);
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "String: " + x + ", parsed to URI: " + u);
}
return u;
} catch (ServletParseException e) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "String: " + x + ", try parse as Address instead");
}
return nameAddrCreateURI(x);
}
}
/**
* Transform (resolve) a URI (Tel-URL or SIP-URI with phone number) to a Sip-URI.
* Normalizes a possible phone number and perform an ENUM lookup.
*
* @param uri the URI to resolve
* @return SipURI or null
*/
public SipURI transformURI(URI uri) {
SipURI sipUri = null;
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "transformURI uri: " + uri);
}
if (uri == null) {
return null;
}
if (UriUtil.isTelephoneNumber(uri)) {
try {
sipUri = resolver.lookupSipURI(uri);
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "uri: " + uri);
logger.log(Level.FINER, "resolved to: " + sipUri);
}
} catch (IOException ioe) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "failed to resolve: " + uri, ioe);
}
} catch (TelUrlResolverException telExc) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "TelUrlResolverException, failed to resolve: " + uri, telExc);
}
}
} else {
if (uri.isSipURI()) {
sipUri = (SipURI) uri;
}
}
return (SipURI) UriUtil.cleanupPossibleTelephoneNumberURI(sipUri);
}
/**
* Returns the most significant user part of a given comma-separated
* collection of URIs, or null if no usable user part can be found.
* The URI will be canonicalized.
*
* @return
*/
public String canonicalizeAndTransformMultiURIgetUser(String uris) {
SipURI su = getUsableSipUri(new AddressTokenizer(uris), true, uris);
if (su == null) {
return getBestTelUriPhoneNumber(new AddressTokenizer(uris));
} else {
return su.getUser();
}
}
private String getBestTelUriPhoneNumber(AddressTokenizer t) {
while (t.hasMoreTokens()) {
AddressTokenizer.Token t0 = t.nextToken();
URI canonicalizedUri = null;
try {
switch (t0.code) {
case 0:
case 1:
canonicalizedUri = canonicalize(forgivingCreateURI(t0.token));
break;
case 2:
canonicalizedUri = canonicalize(nameAddrCreateURI(t0.token));
break;
}
if (UriUtil.isTelephoneNumber(canonicalizedUri)) {
TelURL telUrl = null;
if (canonicalizedUri.isSipURI()) {
try {
telUrl = UriUtil.convertToTelURL((SipURI) canonicalizedUri);
} catch (UriUtilException e) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Exception when converting to Tel-URL: " + t0.token);
}
// Continue with next token
}
} else if (UriUtil.isTelUrl(canonicalizedUri)) {
telUrl = (TelURL) canonicalizedUri;
} else {
return null;
}
if (telUrl != null) {
return ((TelURL) normalize(telUrl)).getPhoneNumber();
}
}
} catch (ServletParseException e) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Exception when parsing this one: " + t0.token);
}
}
}
return null;
}
/**
* Try to find a SipURI with a non-empty "user" string usable as a hash key.
* Addresses are tried in this order: first all addresses that are literally
* SIP URIs; then all other URIs, such as TEL URLs. All addresses are
* subjected to canonicalization and resolution by the transformURI method.
*
* @param t an AddressTokenizer
* @param considerSipUris true means consider literally SIP URIs only, false
* means consider non-SIP only.
* @param uris the string of unparsed addresses
* @return a usable SipURI, or null in case of failure.
*/
private SipURI getUsableSipUri(AddressTokenizer t, boolean considerSipUris, String uris) {
if (!t.hasMoreTokens()) {
return (considerSipUris == false) ? null : getUsableSipUri(new AddressTokenizer(uris), false, null);
} else {
AddressTokenizer.Token t0 = t.nextToken();
URI u0 = null;
try {
switch (t0.code) {
case 0:
case 1:
u0 = canonicalize(forgivingCreateURI(t0.token));
break;
case 2:
u0 = canonicalize(nameAddrCreateURI(t0.token));
break;
}
} catch (ServletParseException e) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "multiple URIs, exception when parsing this one: " + t0.token);
}
// Continue with next
return getUsableSipUri(t, considerSipUris, uris);
}
if ((u0 == null) || (u0.isSipURI() && !considerSipUris) || (!u0.isSipURI() && considerSipUris)) {
return getUsableSipUri(t, considerSipUris, uris);
} else {
SipURI su0 = transformURI(u0);
if ((su0 == null) || (su0.getUser() == null) || su0.getUser().equals("")) {
return getUsableSipUri(t, considerSipUris, uris);
} else {
return su0;
}
}
}
}
/**
* Call this method when we already know that the string to be parsed is a
* name-addr.
*
* @param x
* @return
* @throws ServletParseException
*/
private URI nameAddrCreateURI(String x) throws ServletParseException {
Address a = sf.createAddress(x);
URI u = a.getURI();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "string: " + x + ", parsed to Address: " + a + ", and URI: " + u);
}
return u;
}
/**
* Canonicalize the specified URI (see {@link UriTools}). Also ensures that a possible telephone number is cleaned (see {@link UriUtil#cleanupPossibleTelephoneNumberURI(URI)}.
* @param uri the URI to canonicalize
* @return the resulting URI
*/
public URI canonicalize(URI uri) {
return UriUtil.isTelephoneNumber(uri) ? uriUtil.canonicalize(UriUtil.cleanupPossibleTelephoneNumberURI(uri)) : uriUtil.canonicalize(uri);
}
/**
* Canonicalize the specified URI (see {@link UriTools})
* @param uriStr the URI to canonicalize; as a string
* @return the resulting URI
*/
public URI canonicalize(String uriStr) {
if (uriStr == null) {
return null;
}
try {
URI uri = forgivingCreateURI(uriStr);
return canonicalize(uri);
} catch (ServletParseException e) {
// Return null at failure
logger.log(Level.WARNING, "clb.sip.exception_transforming_uri",
e.getMessage());
if(logger.isLoggable(Level.FINE)){
logger.log(Level.FINE, "clb.caught_an_exception", e);
}
}
return null;
}
/**
* Gets the user part or phone number from the URI. If it is a Tel-URL the phone number is considered to be the user.
* @param uri the user
* @return the user part from the URI.
*/
public String getUserOrPhoneNumber(URI uri) {
if (uri == null) {
return null;
}
if (uri.isSipURI() && !UriUtil.isTelephoneNumber(uri)) {
return ((SipURI) uri).getUser();
} else if (uri.isSipURI() && UriUtil.isTelephoneNumber(uri)) {
TelURL telUrl;
try {
telUrl = UriUtil.convertToTelURL((SipURI) uri);
return telUrl.getPhoneNumber();
} catch (UriUtilException e) {
throw new IllegalStateException("Unknown type of Uri:" + uri, e);
}
} else if (UriUtil.isTelUrl(uri)) {
return ((TelURL) uri).getPhoneNumber();
} else {
return null;
}
}
/**
* Normalizes (i.e., makes phone number global) the specified Tel-URI.
* @param uri the URI to normalize
* @return the normalized Tel-URI
*/
public URI normalize(URI uri) {
if (uri == null) {
return null;
}
if (!UriUtil.isTelephoneNumber(uri)) {
return uri;
}
return internalNormalize(uri);
}
private URI internalNormalize(URI uri) {
assert UriUtil.isTelephoneNumber(uri);
TelURL telUrl;
if (uri.isSipURI()) {
try {
telUrl = UriUtil.convertToTelURL((SipURI) uri);
} catch (UriUtilException e) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Failed to convert SIP-URI to Tel-URL: ", e);
}
return null;
}
} else if (UriUtil.isTelUrl(uri)) {
telUrl = (TelURL) uri;
} else {
return uri;
}
if (!telUrl.isGlobal()) {
String globalNumber = resolver.normalize(telUrl.getPhoneNumber(), telUrl.getPhoneContext());
if (globalNumber != null) {
telUrl.setPhoneNumber(globalNumber);
telUrl.removeParameter(TelURLImpl.PHONE_CONTEXT);
}
}
if (uri.isSipURI()) {
SipURI sipUri = (SipURI) uri.clone();
sipUri.setUser(UriUtil.getAsSipUriUser(telUrl));
return sipUri;
} else {
return telUrl;
}
}
}