de.iteratec.iteraplan.businesslogic.common.URLBuilder.java Source code

Java tutorial

Introduction

Here is the source code for de.iteratec.iteraplan.businesslogic.common.URLBuilder.java

Source

/*
 * iteraplan is an IT Governance web application developed by iteratec, GmbH
 * Copyright (C) 2004 - 2014 iteratec, GmbH
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY ITERATEC, ITERATEC DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact iteratec GmbH headquarters at Inselkammerstr. 4
 * 82008 Munich - Unterhaching, Germany, or at email address info@iteratec.de.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "iteraplan" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by iteraplan".
 */
package de.iteratec.iteraplan.businesslogic.common;

import java.util.Locale;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;

import org.apache.commons.lang.StringUtils;
import org.springframework.webflow.execution.RequestContext;

import de.iteratec.iteraplan.common.Dialog;
import de.iteratec.iteraplan.common.Logger;
import de.iteratec.iteraplan.common.error.IteraplanTechnicalException;
import de.iteratec.iteraplan.common.util.IteraplanProperties;
import de.iteratec.iteraplan.elasticeam.metamodel.UniversalTypeExpression;
import de.iteratec.iteraplan.elasticeam.model.UniversalModelExpression;
import de.iteratec.iteraplan.elasticeam.operator.filter.FilteredUniversalType;
import de.iteratec.iteraplan.elasticmi.metamodel.common.ElasticMiConstants;
import de.iteratec.iteraplan.elasticmi.metamodel.read.RPropertyExpression;
import de.iteratec.iteraplan.elasticmi.metamodel.read.RStructuredTypeExpression;
import de.iteratec.iteraplan.elasticmi.model.ObjectExpression;
import de.iteratec.iteraplan.model.BusinessMapping;
import de.iteratec.iteraplan.model.Isr2BoAssociation;
import de.iteratec.iteraplan.model.Tcr2IeAssociation;
import de.iteratec.iteraplan.model.Transport;
import de.iteratec.iteraplan.model.TypeOfBuildingBlock;
import de.iteratec.iteraplan.model.interfaces.IdEntity;
import de.iteratec.iteraplan.model.interfaces.IdentityEntity;

/**
 * Contains diverse methods to create URL strings to objects in iteraplan.
 */
public final class URLBuilder {

    private static final Logger LOGGER = Logger.getIteraplanLogger(URLBuilder.class);

    private static final int WEB_PORT = 80;
    private static final String COLON = ":";
    public static final String FORWARD_SLASH = "/";

    private static final int BUFFER_SIZE = 150;

    // MVC specific URL fragment
    public static final String INIT_DO = "/init.do?id=";

    public static final String FLOW_MAPPING = "/show/";

    // Contains the server and application url. Is set in iteraplan.properties when using e.g. a
    // reverse proxy server.
    private static final String APPLICATION_ADDRESS_FROM_PROPERTIES = IteraplanProperties.getProperties()
            .getProperty(IteraplanProperties.APPLICATION_ADDRESS_FROM_PROPERTIES);

    private URLBuilder() {
        // no instance needed. Only providing static methods
    }

    public static enum EntityRepresentation {
        FLOW_HTML {
            @Override
            public String getUriComponent() {
                return "";
            }
        },

        JSON {
            @Override
            public String getUriComponent() {
                return "json" + FORWARD_SLASH;
            }
        };

        public abstract String getUriComponent();
    }

    /**
     * Returns the URL of the IdentityEntity object by using the {@link Dialog} enum and extracting
     * the server address out of the flow's RequestContext.
     * 
     * @param entity
     *          An IdentityEntity object.
     * @param context
     *          The current RequestContext of the flow.
     * @return The URL of the BuildingBlock object as String.
     */
    public static String getEntityURL(IdentityEntity entity, RequestContext context) {
        return getEntityURL(entity, (HttpServletRequest) context.getExternalContext().getNativeRequest());
    }

    /**
     * Returns the URL of the IdentityEntity object by using the {@link Dialog} enum.
     * This method links to the HTML/ WebFlow representation of the entity.
     * 
     * @param entity
     *          An IdentityEntity object.
     * @param request
     *          The current HttpServletRequest
     * @return The URL of the BuildingBlock object as String.
     */
    public static String getEntityURL(IdentityEntity entity, HttpServletRequest request) {
        return getEntityURL(entity, request, EntityRepresentation.FLOW_HTML);
    }

    /**
     * Returns the URL of the IdentityEntity object by using the {@link Dialog} enum
     * 
     * @param entity
     *          An IdentityEntity object.
     * @param request
     *          The current HttpServletRequest
     * @param representation
     *          The presentation form of the entity that should be linked to
     * @return The URL of the BuildingBlock object as String.
     */
    public static String getEntityURL(IdentityEntity entity, HttpServletRequest request,
            EntityRepresentation representation) {
        String serverURL = getApplicationURL(request);

        return getEntityURL(entity, serverURL, representation);
    }

    /**
     * Returns the URL of the IdentityEntity object by using the {@link Dialog} enum in combination
     * with a given server address. This method links to the HTML/ WebFlow representation of the entity.
     * 
     * @param entity
     *          An IdentityEntity object.
     * @param serverURL
     *          The known server + application url. Should look like https://127.0.0.1:8443/iteraplan
     * @return The URL of the BuildingBlock object as String.
     */
    public static String getEntityURL(IdentityEntity entity, String serverURL) {
        return getEntityURL(entity, serverURL, EntityRepresentation.FLOW_HTML);
    }

    /**
     * Returns the URL of the IdentityEntity object by using the {@link Dialog} enum in combination
     * with a given server address.
     * 
     * @param entity
     *          An IdentityEntity object.
     * @param serverURL
     *          The known server + application url. Should look like https://127.0.0.1:8443/iteraplan
     * @param representation
     *          The presentation form of the entity that should be linked to
     * @return The URL of the BuildingBlock object as String.
     */
    public static String getEntityURL(IdentityEntity entity, String serverURL,
            EntityRepresentation representation) {
        StringBuilder url = new StringBuilder(BUFFER_SIZE);

        // iteraplan properties are checked for server address override
        url.append(StringUtils.defaultIfEmpty(APPLICATION_ADDRESS_FROM_PROPERTIES, serverURL));

        LOGGER.info("The application address from the properties is: " + APPLICATION_ADDRESS_FROM_PROPERTIES);
        LOGGER.info("URL created from the application address: " + url.toString());

        IdEntity linkTarget = deriveLinkTarget(entity);

        String type = Dialog.dialogNameForClass(linkTarget.getClass());
        if (type == null) { // entity class is still unknown --> no link
            return "";
        }
        type = type.toLowerCase(Locale.ENGLISH);
        url.append(FLOW_MAPPING);
        url.append(type);
        url.append(FORWARD_SLASH);
        url.append(representation.getUriComponent());
        url.append(linkTarget.getId());

        return url.toString();
    }

    public static String getEntityURL(UniversalModelExpression instance, UniversalTypeExpression type,
            String serverURL) {
        TypeOfBuildingBlock tobb = null;

        try {
            tobb = getTOBBForUTE(type);
        } catch (IteraplanTechnicalException e) {
            return ""; //no link for unknown type
        }

        StringBuilder url = new StringBuilder(BUFFER_SIZE);

        // iteraplan properties are checked for server address override
        url.append(StringUtils.defaultIfEmpty(APPLICATION_ADDRESS_FROM_PROPERTIES, serverURL));

        LOGGER.info("The application address from the properties is: " + APPLICATION_ADDRESS_FROM_PROPERTIES);
        LOGGER.info("URL created from the application address: " + url.toString());

        String typeName = Dialog.dialogNameForClass(tobb.getAssociatedClass()).toLowerCase(Locale.ENGLISH);
        url.append(FLOW_MAPPING);
        url.append(typeName);
        url.append(FORWARD_SLASH);
        url.append(instance.getValue(UniversalTypeExpression.ID_PROPERTY));

        return url.toString();
    }

    public static String getEntityURL(ObjectExpression instance, RStructuredTypeExpression type, String serverURL) {
        TypeOfBuildingBlock tobb = null;

        try {
            tobb = getTOBBForStructuredType(type);
        } catch (IteraplanTechnicalException e) {
            return ""; //no link for unknown type
        }

        StringBuilder url = new StringBuilder(BUFFER_SIZE);

        // iteraplan properties are checked for server address override
        url.append(StringUtils.defaultIfEmpty(APPLICATION_ADDRESS_FROM_PROPERTIES, serverURL));

        LOGGER.info("The application address from the properties is: " + APPLICATION_ADDRESS_FROM_PROPERTIES);
        LOGGER.info("URL created from the application address: " + url.toString());

        String typeName = Dialog.dialogNameForClass(tobb.getAssociatedClass()).toLowerCase(Locale.ENGLISH);
        url.append(FLOW_MAPPING);
        url.append(typeName);
        url.append(FORWARD_SLASH);
        RPropertyExpression idProperty = type.findPropertyByPersistentName(ElasticMiConstants.PERSISTENT_NAME_ID);
        url.append(idProperty.apply(instance).getOne().asInteger());

        return url.toString();
    }

    private static TypeOfBuildingBlock getTOBBForUTE(UniversalTypeExpression type) {
        UniversalTypeExpression baseType = type;

        while (baseType instanceof FilteredUniversalType<?>) {
            baseType = ((FilteredUniversalType<?>) baseType).getBaseType();
        }

        String persistentName = baseType.getPersistentName();

        persistentName = persistentName.replace("InformationFlow", "Transport");

        return TypeOfBuildingBlock.fromInitialCapString(persistentName);
    }

    private static TypeOfBuildingBlock getTOBBForStructuredType(RStructuredTypeExpression type) {
        RStructuredTypeExpression baseType = type.getCanonicBase();

        String persistentName = baseType.getPersistentName();

        persistentName = persistentName.replace("InformationFlow", "Transport");

        return TypeOfBuildingBlock.fromInitialCapString(persistentName);
    }

    /**
     * Checks if the passed entity is suitable for linking to (typically yes). For some exceptional
     * cases, it will find an entity that can be linked.
     * 
     * @param entity
     *          A model entity
     * @return Typically the same object as passed in. For some classes, a related object is returned,
     *         which supports linking
     */
    private static IdEntity deriveLinkTarget(IdEntity entity) {
        // get url element of building block type: consider exceptions from the rule
        IdEntity linkTarget = entity;
        if (entity instanceof BusinessMapping) {
            linkTarget = ((BusinessMapping) entity).getInformationSystemRelease();
        } else if (entity instanceof Transport) {
            linkTarget = ((Transport) entity).getInformationSystemInterface();
        } else if (entity instanceof Tcr2IeAssociation) {
            linkTarget = ((Tcr2IeAssociation) entity).getTechnicalComponentRelease();
        } else if (entity instanceof Isr2BoAssociation) {
            linkTarget = ((Isr2BoAssociation) entity).getInformationSystemRelease();
        }
        return linkTarget;
    }

    /**
     * Retrieves the URL of the application. It should be something like:
     * https://localhost:8443/iteraplan
     * 
     * @param req
     *          The current servlet request providing the necessary information.
     * @return The constructed application URL. Without trailing slash.
     */
    public static String getApplicationURL(HttpServletRequest req) {
        StringBuilder appUrlBuffer = new StringBuilder(BUFFER_SIZE);

        // Iteraplan properties are checked for server address override
        if (StringUtils.isNotEmpty(APPLICATION_ADDRESS_FROM_PROPERTIES)) {
            appUrlBuffer.append(APPLICATION_ADDRESS_FROM_PROPERTIES);
        } else {
            appUrlBuffer.append(getServerURL(req));
            appUrlBuffer.append(req.getContextPath());
        }

        return appUrlBuffer.toString();
    }

    /**
     * Retrieves the URL of the server. It is also needed in case of ServletReqeusts, where
     * ContextPath is not available, so URL is without application name. It should be something like:
     * https://localhost:8443 This method is private because the iteraplan.properties are not checked
     * to see if the user has entered an application url by hand as in the other methods. Therefore
     * there could be problems if this method is used directly and the user depends on the property
     * setting.
     * 
     * @param req
     *          The current servlet request providing the necessary information.
     * @return The constructed application URL.
     */
    private static String getServerURL(ServletRequest req) {
        StringBuilder serverUrl = new StringBuilder(BUFFER_SIZE);

        serverUrl.append(req.getScheme());
        serverUrl.append(COLON);
        serverUrl.append(FORWARD_SLASH);
        serverUrl.append(FORWARD_SLASH);
        serverUrl.append(req.getServerName());

        int port = req.getServerPort();
        if (port != WEB_PORT) {
            serverUrl.append(COLON);
            serverUrl.append(Integer.toString(port));
        }

        return serverUrl.toString();
    }

    private static String getRelativeURLforFlow(String context, String dialog, Integer objectId) {
        StringBuilder relativeUrlBuffer = new StringBuilder(BUFFER_SIZE);

        relativeUrlBuffer.append(context);
        relativeUrlBuffer.append(FLOW_MAPPING);
        relativeUrlBuffer.append(StringUtils.lowerCase(dialog));
        relativeUrlBuffer.append(FORWARD_SLASH);
        relativeUrlBuffer.append(objectId);

        return relativeUrlBuffer.toString();
    }

    /**
     * This method returns the relative URL of the object's flow by combining application name, object
     * name and object id. Only use this overloaded method, if
     * {@link #getRelativeURLforFlow(String, IdEntity)} does not work for you.
     * 
     * @param context
     *          The application name. Should be something like /iteraplan.
     * @param entity
     *          The object to link to.
     * @param targetClassHint
     *          The class of the object, if you want to override auto-detection. This class is
     *          determined to derive a dialog that is appropriate for managing <code>entity</code>.
     * @return The relative URL of the flow.
     */
    public static String getRelativeURLforFlow(String context, IdEntity entity,
            Class<? extends IdEntity> targetClassHint) {
        String dialog = Dialog.dialogNameForClass(targetClassHint);
        return getRelativeURLforFlow(context, dialog, entity.getId());
    }

    /**
     * This method returns the relative URL of the object's flow by combining application name, object
     * name and object id. If the objects cannot be linked to directly, this method determines a
     * related object and will generate a link to that one.
     * <dl>
     * For instance, a business mapping will be replaced by its information system.
     * </dl>
     * 
     * @param context
     *          The application name. Should be something like /iteraplan.
     * @param entity
     *          The object to link to.
     * @return The relative URL of the flow.
     */
    public static String getRelativeURLforFlow(String context, IdEntity entity) {
        IdEntity linkTarget = deriveLinkTarget(entity);
        return getRelativeURLforFlow(context, linkTarget, linkTarget.getClass());
    }

    private static String getRelativeURLforMVC(String context, String dialog, Integer objectId) {
        StringBuilder relativeUrlBuffer = new StringBuilder(BUFFER_SIZE);

        // currently no HTML escaping necessary as no conflicting characters are used
        relativeUrlBuffer.append(context);
        relativeUrlBuffer.append(FORWARD_SLASH);
        relativeUrlBuffer.append(dialog.toLowerCase());
        relativeUrlBuffer.append(INIT_DO);
        relativeUrlBuffer.append(objectId);

        return relativeUrlBuffer.toString();
    }

    /**
     * This method returns the relative URL of the object's MVC page by combining application name,
     * object name and object id. If the objects cannot be linked to directly, this method determines
     * a related object and will generate a link to that one.
     * <dl>
     * For instance, a business mapping will be replaced by its information system.
     * </dl>
     * 
     * @param context
     *          The application name. Should be something like /iteraplan.
     * @param entity
     *          The object to link to.
     * @return The relative URL for the MVC page.
     */
    public static String getRelativeURLforMVC(String context, IdEntity entity) {
        IdEntity linkTarget = deriveLinkTarget(entity);
        String dialog = Dialog.dialogNameForClass(linkTarget.getClass());
        return getRelativeURLforMVC(context, dialog, linkTarget.getId());
    }

    /**
     * Returns the absolute URL of the object's flow page by extracting the server and application url
     * out of the pageContext and adding name, id and flow specific String elements.
     * 
     * @param pageContext
     *          The current PageContext.
     * @param dialog
     *          The object's name.
     * @param objectId
     *          The object's id.
     * @return The absolute URL of the flow.
     */
    public static String getAbsoluteURLforFlow(PageContext pageContext, String dialog, Integer objectId) {
        String applicationUrl = getApplicationURL((HttpServletRequest) pageContext.getRequest());

        return getRelativeURLforFlow(applicationUrl, dialog, objectId);
    }

}