root/usr/src/lib/libslp/javalib/com/sun/slp/SLPTemplateRegistry.java
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 *
 */

//  SLPTemplateRegistry.java: Service object for registering a new service
//                        template.
//  Author:           James Kempf
//  Created On:       Tue May 27 15:04:35 1997
//  Last Modified By: James Kempf
//  Last Modified On: Thu Jan  7 14:25:20 1999
//  Update Count:     134
//

package com.sun.slp;

import java.util.*;

/**
 * The SLPTemplateRegistry class registers and unregisters service templates,
 * looks up the template based on the service type name, and returns an
 * attribute verifier for the service.It subclasses the TemplateRegistry
 * abstract class.
 *
 * An slp-template URL has the following format:
 *
 *   service:slp-template:<document URL>;type=<service type>;
 *                                        version=<version no.>;
 *                                        language=<language locale>
 *
 * @author James Kempf
 *
 */

class SLPTemplateRegistry extends TemplateRegistry {

    /**
     * Attribute id for attribute describing service type name.
     * String, single valued attribute.
     */

    static final String SERVICE_ATTR_ID = "template-type";

    /**
     * Attribute id for attribute describing help text.
     * String, single valued, required attribute, .
     */

    static final String DESCRIPTION_ATTR_ID = "template-description";

    /**
     * Attribute id for attribute describing service version. The
     * version number is of the form ``n.m'', where n and m are integers.
     * String, single valued, required attribute.
     */

    static final String VERSION_ATTR_ID = "template-version";

    /**
     * Attribute id for attribute describing service URL url part grammer.
     * String, single valued, required attribute.
     */

    static final String SERVICE_URL_ATTR_ID = "template-url-syntax";

    /**
     * The service type name for the template type.
     */

    static final String TEMPLATE_SERVICE_TYPE = "service:slp-template";

    // The distinguished template registry object.

    private static TemplateRegistry registry = null;

    // Package private constructor for singleton pattern maintained
    // by the ServiceLocationManager.

    SLPTemplateRegistry() throws ServiceLocationException {

    }

    //
    // Public implementation.
    //

    /**
     * Register the new service.
     *
     * @param serviceType       Name of the service.
     * @param documentURL       URL of the template document.
     * @param languageLocale    Locale of the template langugae.
     * @param version           Version number of template document.
     * @exception ServiceLocationException Error code is
     *                              INVALID_REGISTRATION
     *                                     if the service already exists or
     *                                     the registration fails.
     *                                     Throws
     *                              SYSTEM_ERROR
     *                                     if the scope vector is null or
     *                                     empty.
     *                                     Throws
     *                              PARSE_ERROR
     *                                     if an attribute is bad.
     * @exception IllegalArgumentException Thrown if any parameters are null.
     *
     */

    public void registerServiceTemplate(ServiceType serviceType,
                                        String documentURL,
                                        Locale languageLocale,
                                        String version)
        throws ServiceLocationException {

        // Check for illegal parameters.

        Assert.nonNullParameter(serviceType, "serviceType");

        Assert.nonNullParameter(documentURL, "documentURL");

        Assert.nonNullParameter(languageLocale, "language");

        Assert.nonNullParameter(version, "version");

        String language = languageLocale.getLanguage();

        if (language == null || language.length() <= 0) {
            throw
                new IllegalArgumentException(
                SLPConfig.getSLPConfig().formatMessage("template_lang_null",
                                                       new Object[] {
                    documentURL}));
        }

        String turl = null;

        try {

            turl = findTemplateURL(serviceType,
                                   languageLocale,
                                   version);

        } catch (ServiceLocationException ex) {

            // Ignore if language not supported, it just means there
            //  isn't any.

            if (ex.getErrorCode() !=
                ServiceLocationException.LANGUAGE_NOT_SUPPORTED) {
                throw ex;

            }
        }

        // Throw an exception if it exists.

        if (turl != null) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.INVALID_REGISTRATION,
                                "template_already_registered",
                                new Object[] {
                    documentURL,
                        version,
                        languageLocale});
        }

        // Construct attributes for the registration.

        Vector attributes = new Vector();

        // Add the service type name.

        Vector values = new Vector();
        values.addElement(serviceType.toString());
        ServiceLocationAttribute attr =
            new ServiceLocationAttribute(SERVICE_ATTR_ID, values);

        attributes.addElement(attr);

        // Add the version.

        values = new Vector();
        values.addElement(version);
        attr =
            new ServiceLocationAttribute(VERSION_ATTR_ID, values);

        attributes.addElement(attr);

        // Construct a service URL for the template.

        ServiceURL surl =
            new ServiceURL(TEMPLATE_SERVICE_TYPE +
                           ":"+
                           documentURL+
                           ";"+
                           SERVICE_ATTR_ID+
                           "="+
                           serviceType+
                           ";"+
                           VERSION_ATTR_ID+
                           "="+
                           version,
                           ServiceURL.LIFETIME_MAXIMUM);


        // Do the registration.

        Advertiser serviceAgent =
            ServiceLocationManager.getAdvertiser(languageLocale);

        if (serviceAgent == null) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.NOT_IMPLEMENTED,
                                "no_advertiser",
                                new Object[0]);
        }

        serviceAgent.register(surl, attributes);

        // Note that the assumption here is that the URL containing the
        //  path to the template document is written "somehow".
        //  It is up to the client to make sure that the template document
        //  has been written.

    }

    /**
     * Deregister the template for service type.
     *
     * @param serviceType       Name of service.
     * @param <i>languageLocale</i> Language locale of template.
     * @param <i>version</i> Version of the template, null for latest.
     * @exception ServiceLocationException Thrown if the deregistration
     *                                    fails.
     * @exception IllegalArgumentException Thrown if the parameter is null.
     *
     */

    public void deregisterServiceTemplate(ServiceType serviceType,
                                          Locale languageLocale,
                                          String version)
        throws ServiceLocationException {

        // Check the parameter.

        Assert.nonNullParameter(serviceType, "serviceType");

        Assert.nonNullParameter(languageLocale, "languageLocale");

        // Get the template document URL for the service.

        ServiceURL turl = findVersionedURL(serviceType,
                                           languageLocale,
                                           version);

        // If there's no template, then throw an exception.

        if (turl == null) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.INVALID_REGISTRATION,
                                "template_not_registered",
                                new Object[] {
                    serviceType,
                        version,
                        languageLocale});
        }

        // Deregister in all scopes.

        Advertiser serviceAgent =
            ServiceLocationManager.getAdvertiser(languageLocale);

        if (serviceAgent == null) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.NOT_IMPLEMENTED,
                                "no_advertiser",
                                new Object[0]);
        }

        // Deregister the service URL.

        serviceAgent.deregister(turl);

    }



    /**
     * Find the service URL for the service.
     *
     * @param serviceType               Name of service.
     * @param <i>languageLocale</i> Language locale of template.
     * @param <i>version</i> Version of the template, null for latest.
     * @return ServiceURL for the service template. If the service doesn't
     *          exist, returns null.
     * @exception ServiceLocationException Error code is
     *                              SYSTEM_ERROR
     *                                     if the scope vector is null or
     *                                     empty or if more than one
     *                                     template URL is returned.
     * @exception IllegalArgumentException Thrown if any parameters are null.
     *
     */

    public String findTemplateURL(ServiceType serviceType,
                                  Locale languageLocale,
                                  String version)
        throws ServiceLocationException {

        // Check the parameter.

        Assert.nonNullParameter(serviceType, "serviceType");

        Assert.nonNullParameter(languageLocale, "languageLocale");

        ServiceURL turl = findVersionedURL(serviceType,
                                           languageLocale,
                                           version);

        // If nothing returned, then simply return.

        if (turl == null) {
            return null;

        }

        // Form the document URL.

        ServiceType type = turl.getServiceType();
        String url = turl.toString();
        String abstractType = type.getAbstractTypeName();

        if (!abstractType.equals(TEMPLATE_SERVICE_TYPE)) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.PARSE_ERROR,
                                "template_url_malformed",
                                new Object[] {turl});

        }

        // Parse off the URL path.

        int idx = url.indexOf(";"+SERVICE_ATTR_ID+"=");

        if (idx == -1) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.PARSE_ERROR,
                                "template_url_malformed",
                                new Object[] {turl});

        }

        int jdx = TEMPLATE_SERVICE_TYPE.length() + 1; // don't forget :!!!

        // Return the document URL.

        return url.substring(jdx, idx);
    }

    // Return a URL given a version and language locale.

    private ServiceURL findVersionedURL(ServiceType serviceType,
                                        Locale languageLocale,
                                        String version)
        throws ServiceLocationException {

        // Templates should be registered in all scopes. Look for them
        //  in all.

        Vector scopes = ServiceLocationManager.findScopes();

        // Set up query.

        ServiceLocationEnumeration results = null;
        String query = "(" + SERVICE_ATTR_ID + "=" + serviceType + ")";

        if (version != null) {
            query = query + "(" + VERSION_ATTR_ID + "=" + version + ")";

        }

        query = "(&" + query + ")";

        // Get user agent for query.

        Locator userAgent =
            ServiceLocationManager.getLocator(languageLocale);

        if (userAgent == null) {
            throw
                new ServiceLocationException(
                                ServiceLocationException.NOT_IMPLEMENTED,
                                "no_locator",
                                new Object[0]);
        }

        try {
            ServiceType type = new ServiceType(TEMPLATE_SERVICE_TYPE);

            results =
                userAgent.findServices(type,
                                       scopes,
                                       query);

        } catch (ServiceLocationException ex) {

            // If language not supported, it just means none there.

            if (ex.getErrorCode() !=
                ServiceLocationException.LANGUAGE_NOT_SUPPORTED) {
                throw ex;

            }

        }

        // If nothing came back, then return null.

        if (!results.hasMoreElements()) {
            return null;

        }


        ServiceURL turl = null;
        float highest = (float)-1.0;

        // If there's more than one service of this type registered, then
        //  take highest version if version number was null.

        while (results.hasMoreElements()) {
            ServiceURL surl = (ServiceURL)results.nextElement();
            String urlPath = surl.getURLPath();

            if (version == null) {

                // Get the version attribute from the URL.

                String token = ";"+VERSION_ATTR_ID+"=";

                int idx = urlPath.indexOf(token);

                if (idx == -1) { // ignore, there may be more...
                    continue;

                }

                urlPath =
                    urlPath.substring(idx+token.length(), urlPath.length());

                idx = urlPath.indexOf(";");

                if (idx == -1) { // ignore, there may be more...
                    continue;

                }

                String temversion = urlPath.substring(0, idx);
                float current = (float)0.0;

                // Convert to float.

                try {

                    current = Float.valueOf(temversion).floatValue();

                } catch (NumberFormatException ex) {

                    continue;  // ignore, there may be more...

                }

                // Identify if this is the highest version number so far.

                if (current > highest) {
                    turl = surl;
                }

            } else {

                // If we found more than one, may be a problem.

                if (turl != null) {

                    throw
                        new ServiceLocationException(
                                ServiceLocationException.INTERNAL_SYSTEM_ERROR,
                                "template_multiple",
                                new Object[] {
                            serviceType,
                                version,
                                languageLocale});
                }

                turl = surl;
            }
        }

        return turl;
    }

    /**
     * Create an attribute verifier for the template document URL.
     *
     * @param documentURL A URL for the template document URL.
     * @return An attribute verifier for the service
     * @exception ServiceLocationException Throws
     *                                    PARSE_ERROR
     *                                    if any syntax errors
     *                                    are encountered during parsing
     *                                    of service's template definition.
     *                                    Throws
     *                                  SYSTEM_ERROR
     *                                    if URL parsing error occurs.
     *                                    Throws ServiceLocationException
     *                                    if any other errors occur.
     * @exception IllegalArgumentException Thrown if any parameters are null.
     *
     */

    public ServiceLocationAttributeVerifier attributeVerifier(
                                                        String documentURL)
        throws ServiceLocationException {

        // Check the parameter.

        Assert.nonNullParameter(documentURL, "documentURL");

        // Create a URL attribute parser to parse the document.

        return new URLAttributeVerifier(documentURL);
    }

}