class ServiceDescriptionFactory {

    ServiceDescription getServiceDescription() throws ConfigurationException {

        final XPath pathsXPath = this.xPathFactory.newXPath();
        try {
            final Node serviceDescriptionNode = (Node) pathsXPath.evaluate(
                    ConfigurationFileTagsV1.SERVICE_DESCRIPTION_ELEMENT_XPATH,
                    this.configuration, XPathConstants.NODE);

            final String title = getMandatoryElementText(
                    serviceDescriptionNode,
                    ConfigurationFileTagsV1.TITLE_ELEMENT);

            // Start the creation of a service description for a migration
            // service.
            ServiceDescription.Builder builder = new ServiceDescription.Builder(
                    title, Migrate.class.getCanonicalName());

            Property[] serviceProperties = getServiceProperties(serviceDescriptionNode);

            builder.author(getMandatoryElementText(serviceDescriptionNode,
                    ConfigurationFileTagsV1.CREATOR_ELEMENT));

            builder.classname(this.canonicalServiceName);

            builder.description(getOptionalElementText(serviceDescriptionNode,
                    ConfigurationFileTagsV1.DESCRIPTION_ELEMENT));

            final String serviceVersion = getOptionalElementText(
                    serviceDescriptionNode,
                    ConfigurationFileTagsV1.VERSION_ELEMENT);

            final Tool toolDescription = getToolDescriptionElement(serviceDescriptionNode);

            // Get the migration service identifier or create an identifier if
            // it has not been specified in the configuration document.
            String identifier = getOptionalElementText(serviceDescriptionNode,
                    ConfigurationFileTagsV1.IDENTIFIER_ELEMENT);

            if (identifier == null || "".equals(identifier)) {
                
                // Construct an identifier in the form of a MD5 digest of
                // the tool ID, the canonical class name of the migration
                // service and its version number.
                try {
                    final MessageDigest identDigest = MessageDigest
                            .getInstance("MD5");
                    identDigest.update(this.canonicalServiceName.getBytes());

                    final String versionInfo = (serviceVersion != null) ? serviceVersion
                            : "";
                    identDigest.update(versionInfo.getBytes());

                    final URI toolIDURI = toolDescription.getIdentifier();
                    final String toolIdentifier = toolIDURI == null ? ""
                            : toolIDURI.toString();
                    identDigest.update(toolIdentifier.getBytes());

                    final BigInteger md5hash = new BigInteger(identDigest.digest());
                    identifier = md5hash.toString(16);

                } catch (NoSuchAlgorithmException nsae) {
                    // There is nothing we can do...
                    throw new RuntimeException(nsae);
                }
            }

            builder.identifier(identifier);
            builder.version(serviceVersion);
            builder.tool(toolDescription);

            builder.instructions(getOptionalElementText(serviceDescriptionNode,
                    ConfigurationFileTagsV1.INSTRUCTIONS_ELEMENT));

            builder.furtherInfo(getOptionalURIElement(serviceDescriptionNode,
                    ConfigurationFileTagsV1.FURTHER_INFO_ELEMENT));

            builder.logo(getOptionalURIElement(serviceDescriptionNode,
                    ConfigurationFileTagsV1.LOGO_ELEMENT));

            builder.serviceProvider(this.serviceProvider);

            final DBMigrationPathFactory migrationPathFactory = new DBMigrationPathFactory(
                    this.configuration);

            final MigrationPaths migrationPaths = migrationPathFactory
                    .getAllMigrationPaths();

            builder
                    .paths(MigrationPathConverter
                            .toPlanetsPaths(migrationPaths));

            builder.inputFormats(migrationPaths.getInputFormatURIs().toArray(
                    new URI[0]));

            builder.parameters(getUniqueParameters(migrationPaths));

            builder.properties(serviceProperties);

            return builder.build();

        } catch (XPathExpressionException xPathExpressionException) {
            throw new ConfigurationException(String.format(
                    "Failed parsing the '%s' element in the '%s' element.",
                    ConfigurationFileTagsV1.SERVICE_DESCRIPTION_ELEMENT_XPATH,
                    this.configuration.getNodeName()), xPathExpressionException);
        } catch (NullPointerException nullPointerException) {
            throw new ConfigurationException(String.format(
                    "Failed parsing the '%s' element in the '%s' element.",
                    ConfigurationFileTagsV1.SERVICE_DESCRIPTION_ELEMENT_XPATH,
                    this.configuration.getNodeName()), nullPointerException);
        }
    }

      ServiceDescriptionFactory(String canonicalServiceName,
            String serviceProvider, Document wrapperConfiguration);

}