org.openengsb.ui.admin.xlink.ToolChooserPage.java Source code

Java tutorial

Introduction

Here is the source code for org.openengsb.ui.admin.xlink.ToolChooserPage.java

Source

/**
 * Licensed to the Austrian Association for Software Tool Integration (AASTI)
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. The AASTI licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openengsb.ui.admin.xlink;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.RestartResponseException;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.string.StringValue;
import org.openengsb.core.api.OsgiServiceNotAvailableException;
import org.openengsb.core.api.OsgiUtilsService;
import org.openengsb.core.api.context.ContextHolder;
import org.openengsb.core.api.model.ModelDescription;
import org.openengsb.core.api.model.OpenEngSBModelEntry;
import org.openengsb.core.api.xlink.exceptions.DomainNotLinkableException;
import org.openengsb.core.api.xlink.exceptions.OpenXLinkException;
import org.openengsb.core.api.xlink.model.XLinkConnector;
import org.openengsb.core.api.xlink.model.XLinkConnectorView;
import org.openengsb.core.api.xlink.model.XLinkConstants;
import org.openengsb.core.api.xlink.service.ui.ToolChooserLogic;
import org.openengsb.core.api.xlink.service.ui.XLinkMock;
import org.openengsb.core.services.xlink.XLinkUtils;
import org.openengsb.core.util.ModelUtils;
import org.ops4j.pax.wicket.api.PaxWicketBean;
import org.ops4j.pax.wicket.api.PaxWicketMountPoint;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Manages the processing of an incoming XLink and, if necessary, renders a page
 * where the user can choose between available Views for XLinking.
 * TODO [OPENENGSB-3267] Hook filter onto this WebPage
 */
@PaxWicketMountPoint(mountPoint = "openXLink")
public class ToolChooserPage extends WebPage {

    @PaxWicketBean(name = "osgiUtilsService")
    private OsgiUtilsService serviceUtils;

    @PaxWicketBean(name = "toolChooserLogic")
    private ToolChooserLogic toolChooserLogic;

    @PaxWicketBean(name = "xLinkMock")
    private XLinkMock xLinkMock;

    private String contextId;
    private String modelId;
    private String versionId;
    private Calendar expirationDate;
    private String hostId;
    private String identifier;
    private Object identifierObject;
    private String connectorId;
    private String viewId;

    private Map<String, String[]> requestParameters;

    public ToolChooserPage() {
        processPage();
    }

    public ToolChooserPage(PageParameters parameters) {
        processPage();
    }

    /**
     * Validates the incoming request and manages the further processing of the XLink.
     */
    private void processPage() {
        requestParameters = getRequestParametersAsAMap();
        HttpServletRequest req = (HttpServletRequest) getRequest().getContainerRequest();
        HttpServletResponse resp = (HttpServletResponse) getResponse().getContainerResponse();
        setContextFromId();
        try {
            checkIfXLinkIsValid(req);
        } catch (OpenXLinkException ex) {
            handleErrorResponse(ex.getMessage());
        }
        if (checkForLocalSwitchingParameters()) {
            ModelDescription destinationModelClass = toolChooserLogic.getModelClassOfView(hostId, connectorId,
                    viewId);
            triggerXLinkProcessing(destinationModelClass, connectorId, viewId);
            handleSuccessResponse(resp);
            return;
        }
        buildToolChooserPage(resp);
    }

    /**
     * Returns the Requestparameter as a Map. 
     */
    private Map<String, String[]> getRequestParametersAsAMap() {
        Map<String, String[]> parameterMap = new HashMap<String, String[]>();
        IRequestParameters parameters = getRequest().getQueryParameters();
        for (String key : parameters.getParameterNames()) {
            List<StringValue> values = parameters.getParameterValues(key);
            List<String> valuesAsString = new ArrayList<String>();
            for (StringValue stringvalue : values) {
                valuesAsString.add(stringvalue.toString());
            }
            parameterMap.put(key, (String[]) valuesAsString.toArray(new String[0]));
        }
        return parameterMap;
    }

    /**
     * Returns the value of the given key from the parameterMap or null.
     */
    private String getParameterFromMap(String key) {
        return requestParameters.get(key) == null ? null : requestParameters.get(key)[0];
    }

    /**
     * Throws an OpenXLinkException, if the calling request was not a well-formed XLink,
     * does not contain the mandatory xlink parameters or does not contain the necessary IdentifyingFields
     * to the defined ModelClass.
     */
    private void checkIfXLinkIsValid(HttpServletRequest req) throws OpenXLinkException {
        fetchXLinkParameters(req);
        checkMandatoryXLinkParameters();
        if (checkForLocalSwitchingParameters()) {
            checkConnectorAndViewExists();
        }
        checkXLinkIsExpired();
        List<String> keyFields = null;
        try {
            keyFields = xLinkMock.getModelIdentifierToModelDescription(modelId, versionId);
        } catch (ClassNotFoundException ex) {
            String errorMsg = new StringResourceModel("error.modelClass.notfound", this, null).getString();
            Logger.getLogger(ToolChooserPage.class.getName()).log(Level.SEVERE, null, ex);
            throw new OpenXLinkException(String.format(errorMsg, ex.getMessage()));
        }
        fetchAndCheckIdentifier(keyFields);
    }

    /**
     * Fetches the mandatory parameters for XLink from the calling request.
     */
    private void fetchXLinkParameters(HttpServletRequest req) {
        contextId = getParameterFromMap(XLinkConstants.XLINK_CONTEXTID_KEY);
        modelId = getParameterFromMap(XLinkConstants.XLINK_MODELCLASS_KEY);
        versionId = getParameterFromMap(XLinkConstants.XLINK_VERSION_KEY);
        viewId = getParameterFromMap(XLinkConstants.XLINK_VIEW_KEY);
        identifier = getParameterFromMap(XLinkConstants.XLINK_IDENTIFIER_KEY);
        expirationDate = XLinkUtils
                .dateStringToCalendar(getParameterFromMap(XLinkConstants.XLINK_EXPIRATIONDATE_KEY));
        hostId = req.getRemoteAddr();
        connectorId = getParameterFromMap(XLinkConstants.XLINK_CONNECTORID_KEY);
    }

    /**
     * Throws an OpenXLinkException, if one or more mandatory parameter for XLink where not supplied.
     */
    private void checkMandatoryXLinkParameters() throws OpenXLinkException {
        String errorMsg = new StringResourceModel("error.missingMandatoryGetParam", this, null).getString();
        if (contextId == null) {
            errorMsg += " " + XLinkConstants.XLINK_CONTEXTID_KEY;
        }
        if (modelId == null) {
            errorMsg += ", " + XLinkConstants.XLINK_MODELCLASS_KEY;
        }
        if (versionId == null) {
            errorMsg += ", " + XLinkConstants.XLINK_VERSION_KEY;
        }
        if (expirationDate == null) {
            errorMsg += ", " + XLinkConstants.XLINK_EXPIRATIONDATE_KEY;
        }
        if (hostId == null) {
            errorMsg += ", " + XLinkConstants.XLINK_HOST_HEADERNAME;
        }
        if (identifier == null) {
            errorMsg += ", " + XLinkConstants.XLINK_IDENTIFIER_KEY;
        }
        if ((contextId == null) || (modelId == null) || (versionId == null) || (expirationDate == null)
                || (hostId == null) || (identifier == null)) {
            throw new OpenXLinkException(errorMsg);
        }
    }

    /**
     * Throws an OpenXLinkException, if the calling XLink has expired.
     */
    private void checkXLinkIsExpired() throws OpenXLinkException {
        if (expirationDate != null ? Calendar.getInstance().after(expirationDate) : false) {
            String expiredMsg = new StringResourceModel("error.xlinkHasExpired", this, null).getString();
            throw new OpenXLinkException(expiredMsg);
        }
    }

    /**
     * Throws an OpenXLinkException, if the supplied connector or viewId are registered for XLink
     * or do not exist.
     */
    private void checkConnectorAndViewExists() throws OpenXLinkException {
        String errorConnectorNotRegistered = new StringResourceModel("error.connectorNotRegistrated", this, null)
                .getString();
        String errorViewNotExisting = new StringResourceModel("error.viewNotExisting", this, null).getString();
        if (!toolChooserLogic.isConnectorRegistrated(hostId, connectorId)) {
            throw new OpenXLinkException(errorConnectorNotRegistered);
        }
        if (!toolChooserLogic.isViewExisting(hostId, connectorId, viewId)) {
            throw new OpenXLinkException(errorViewNotExisting);
        }
    }

    /**
     * Fetches and validates the Paramters from the request, to the defined IdentifyingFields.
     */
    private void fetchAndCheckIdentifier(List<String> identifierKeyNames) throws OpenXLinkException {
        Class clazz;
        try {
            clazz = XLinkUtils.getClassOfOpenEngSBModel(modelId, versionId, serviceUtils);
        } catch (ClassNotFoundException ex) {
            String errorMsg = new StringResourceModel("error.modelClass.notfound", this, null).getString();
            throw new OpenXLinkException(errorMsg);
        }
        try {
            ObjectMapper mapper = new ObjectMapper();
            identifierObject = mapper.readValue(identifier, clazz);
        } catch (Exception ex) {
            String errorMsg = new StringResourceModel("error.identifierIsNotValid", this, null).getString();
            throw new OpenXLinkException(errorMsg);
        }
        boolean found;
        for (String key : identifierKeyNames) {
            found = false;
            for (OpenEngSBModelEntry entry : ModelUtils.getOpenEngSBModelEntries(identifierObject)) {
                if (entry.getKey().equals(key)) {
                    found = true;
                    if (entry.getValue() == null) {
                        String errorMsg = new StringResourceModel("error.missingIdentifier", this, null)
                                .getString();
                        throw new OpenXLinkException(String.format(errorMsg, entry.getKey()));
                    }
                }
            }
            if (!found) {
                String errorMsg = new StringResourceModel("error.missingIdentifyingField", this, null).getString();
                throw new OpenXLinkException(String.format(errorMsg, key));
            }
        }
    }

    /**
     * Returns true, if the calling XLink contains the nessecary parameters for 
     * 'local-switching'.
     */
    private boolean checkForLocalSwitchingParameters() {
        return (connectorId != null) && (viewId != null);
    }

    /**
     * Redirects the Request to the Page, that renders the Errormessage to a xlink call.
     */
    private void handleErrorResponse(String error) {
        if (checkForLocalSwitchingParameters()) {
            throw new RestartResponseException(new MachineResponsePage(error, false));
        } else {
            String hostIdMsg = new StringResourceModel("hostId.info", this, null).getString();
            hostIdMsg = String.format(hostIdMsg, hostId);
            throw new RestartResponseException(new UserResponsePage(error, hostIdMsg, true));
        }
    }

    /**
     * Redirects the Request to the Page, that renders the Successmessage to a xlink call.
     */
    private void handleSuccessResponse(HttpServletResponse resp) {
        if (checkForLocalSwitchingParameters()) {
            String successMsg = new StringResourceModel("success.localSwitch", this, null).getString();
            throw new RestartResponseException(new MachineResponsePage(successMsg, true));
        } else {
            String successMsg = new StringResourceModel("success.normalSwitch", this, null).getString();
            String hostIdMsg = new StringResourceModel("hostId.info", this, null).getString();
            hostIdMsg = String.format(hostIdMsg, hostId);
            throw new RestartResponseException(new UserResponsePage(successMsg, hostIdMsg, false));
        }

    }

    /**
     * Builds the Page where the user can choose between the available Views for XLinking.
     */
    private void buildToolChooserPage(final HttpServletResponse resp) {
        String hostIdMsg = new StringResourceModel("hostId.info", this, null).getString();
        hostIdMsg = String.format(hostIdMsg, hostId);
        add(new Label("hostId", hostIdMsg));
        List<XLinkConnector> tools = toolChooserLogic.getRegisteredToolsFromHost(hostId);
        ListView toolList = new ListView("toolList", tools) {
            protected void populateItem(ListItem item) {
                final XLinkConnector tool = (XLinkConnector) item.getModelObject();
                item.add(new Label("toolName", tool.getToolName()));
                ListView viewList = new ListView("viewList", Arrays.asList(tool.getAvailableViews())) {
                    @Override
                    protected void populateItem(ListItem li) {
                        final XLinkConnectorView view = (XLinkConnectorView) li.getModelObject();
                        li.add(new Label("viewName", view.getName()));
                        li.add(new Label("viewDescription", returnLocalizedDescription(view.getDescriptions())));
                        final ModelDescription destModelInfo = toolChooserLogic.getModelClassOfView(hostId,
                                tool.getId(), view.getViewId());
                        Link viewLink = new Link("viewLink") {
                            public void onClick() {
                                triggerXLinkProcessing(destModelInfo, tool.getId(), view.getViewId());
                                handleSuccessResponse(resp);
                            }
                        };
                        if (xLinkMock.isTransformationPossible(modelId, versionId,
                                destModelInfo.getModelClassName(), destModelInfo.getVersionString())) {
                            String labelText = new StringResourceModel("toolchooser.match", this, null).getString();
                            viewLink.add(new Label("viewLinkLabel", labelText));
                        } else {
                            viewLink.setEnabled(false);
                            String labelText = new StringResourceModel("toolchooser.nomatch", this, null)
                                    .getString();
                            viewLink.add(new Label("viewLinkLabel", labelText));
                        }
                        li.add(viewLink);
                    }
                };
                item.add(viewList);
            }
        };
        add(toolList);
    }

    /**
     * Triggers the processing of the supplied XLink
     */
    private void triggerXLinkProcessing(ModelDescription destModelInfo, String connectorId, String viewId) {
        List<Object> modelObjectsDestination = new ArrayList<Object>();
        try {
            modelObjectsDestination = xLinkMock.transformModelObject(modelId, versionId,
                    destModelInfo.getModelClassName(), destModelInfo.getVersionString(), identifierObject);
        } catch (ClassNotFoundException ex) {
            String errorMsg = new StringResourceModel("error.modelClass.notfound", this, null).getString();
            handleErrorResponse(String.format(errorMsg, ex.getMessage()));
        } catch (OpenXLinkException ex) {
            String errorMsg = new StringResourceModel("error.transformationNotPossible", this, null).getString();
            handleErrorResponse(String.format(errorMsg, ex.getMessage()));
        }
        try {
            if (!modelObjectsDestination.isEmpty()) {
                xLinkMock.openPotentialMatches(modelObjectsDestination, connectorId, viewId);
            }
        } catch (OsgiServiceNotAvailableException ex) {
            String errorMsg = new StringResourceModel("error.connectorNotFound", this, null).getString();
            handleErrorResponse(errorMsg);
        } catch (DomainNotLinkableException ex) {
            String errorMsg = new StringResourceModel("error.connectorNotLinkable", this, null).getString();
            handleErrorResponse(errorMsg);
        } catch (OpenXLinkException ex) {
            String errorMsg = new StringResourceModel("error.connectorNotFound", this, null).getString();
            handleErrorResponse(errorMsg);
        }
    }

    private void setContextFromId() {
        ContextHolder.get().setCurrentContextId(contextId);
    }

    private void setLocale(Locale locale) {
        if (locale != null) {
            getSession().setLocale(locale);
        }
    }

    /**
     * Returns the description for the currently configured locale. 
     * If no description could be found for the configured locale, the first one
     * from the iterator is returned.
     */
    private String returnLocalizedDescription(Map<String, String> descriptions) {
        if (descriptions.isEmpty()) {
            return null;
        }
        if (!descriptions.containsKey(getLocaleKey())) {
            return descriptions.values().iterator().next();
        }
        return descriptions.get(getLocaleKey());
    }

    /**
     * Returns the currently configured locale key.
     */
    private String getLocaleKey() {
        return getLocale().getLanguage();
    }

    public void setServiceUtils(OsgiUtilsService serviceUtils) {
        this.serviceUtils = serviceUtils;
    }

}