/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed 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 com.evolveum.midpoint.web.page.admin.resources;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.model.NonEmptyLoadableModel;
import com.evolveum.midpoint.gui.api.model.NonEmptyModel;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.web.application.AuthorizationAction;
import com.evolveum.midpoint.web.application.PageDescriptor;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.component.wizard.Wizard;
import com.evolveum.midpoint.web.component.wizard.WizardStep;
import com.evolveum.midpoint.web.component.wizard.resource.*;
import com.evolveum.midpoint.web.page.error.PageError;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.wizard.IWizardModel;
import org.apache.wicket.extensions.wizard.WizardModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* @author lazyman
*/
@PageDescriptor(url = "/admin/resources/wizard", action = {
@AuthorizationAction(actionUri = PageAdminResources.AUTH_RESOURCE_ALL,
label = PageAdminResources.AUTH_RESOURCE_ALL_LABEL,
description = PageAdminResources.AUTH_RESOURCE_ALL_DESCRIPTION),
@AuthorizationAction(actionUri = AuthorizationConstants.AUTZ_UI_RESOURCE_EDIT_URL,
label = "PageResourceWizard.auth.resource.label",
description = "PageResourceWizard.auth.resource.description")})
public class PageResourceWizard extends PageAdminResources {
private static final String ID_WIZARD = "wizard";
public static final String PARAM_OID = "oid";
public static final String PARAM_CONFIG_ONLY = "configOnly";
public static final String PARAM_READ_ONLY = "readOnly";
// these models should be reset after each 'save' operation, in order to fetch current data (on demand)
// each step should use corresponding model
// these models have always non-null content
@NotNull private final NonEmptyLoadableModel<PrismObject<ResourceType>> modelRaw; // contains resolved connector as well
@NotNull private final NonEmptyLoadableModel<PrismObject<ResourceType>> modelNoFetch; // contains resolved connector as well
@NotNull private final NonEmptyLoadableModel<PrismObject<ResourceType>> modelFull;
@NotNull private final ResourceWizardIssuesModel issuesModel;
// additional models that have to be reset after each 'save' operation
@NotNull private final Collection<LoadableModel<?>> dependentModels = new HashSet<>();
// for new resources: should be set after first save; for others: should be set on page creation
private String editedResourceOid;
private final boolean configurationOnly;
private boolean readOnly;
public PageResourceWizard(@NotNull PageParameters parameters) {
getPageParameters().overwriteWith(parameters); // to be available in the methods called within this constructor as well
configurationOnly = parameters.get(PARAM_CONFIG_ONLY).toBoolean();
readOnly = parameters.get(PARAM_READ_ONLY).toBoolean();
editedResourceOid = getResourceOid(); // might be null at this moment
LOGGER.debug("Resource wizard called with oid={}, configOnly={}, readOnly={}", editedResourceOid, configurationOnly, readOnly);
modelRaw = createResourceModel(Arrays.asList(
SelectorOptions.create(ResourceType.F_CONNECTOR_REF, GetOperationOptions.createResolve()),
SelectorOptions.create(GetOperationOptions.createRaw())));
modelNoFetch = createResourceModel(Arrays.asList(
SelectorOptions.create(ResourceType.F_CONNECTOR_REF, GetOperationOptions.createResolve()),
SelectorOptions.create(GetOperationOptions.createNoFetch())));
modelFull = createResourceModel(null);
issuesModel = new ResourceWizardIssuesModel(modelFull, this);
initLayout();
}
@Override
protected void createBreadcrumb() {
createInstanceBreadcrumb();
}
@NotNull
private NonEmptyLoadableModel<PrismObject<ResourceType>> createResourceModel(final Collection<SelectorOptions<GetOperationOptions>> options) {
return new NonEmptyLoadableModel<PrismObject<ResourceType>>(false) {
@NotNull
@Override
protected PrismObject<ResourceType> load() {
try {
if (editedResourceOid == null) {
return getPrismContext().createObject(ResourceType.class);
}
PrismObject<ResourceType> resource = loadResourceModelObject(options);
if (resource == null) {
throw new RestartResponseException(PageError.class);
}
return resource;
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load resource", ex);
throw new RestartResponseException(PageError.class);
}
}
};
}
// named differently from "loadResource", as this is used in the superclass
private PrismObject<ResourceType> loadResourceModelObject(Collection<SelectorOptions<GetOperationOptions>> options) {
Task task = createSimpleTask("loadResource");
return WebModelServiceUtils.loadObject(ResourceType.class, editedResourceOid, options, this, task, task.getResult());
}
public String getEditedResourceOid() {
return editedResourceOid;
}
public void setEditedResourceOid(String editedResourceOid) {
this.editedResourceOid = editedResourceOid;
}
@Override
protected IModel<String> createPageTitleModel() {
return new LoadableModel<String>(false) {
@Override
protected String load() {
if (editedResourceOid == null) {
return PageResourceWizard.super.createPageTitleModel().getObject();
}
String name = WebComponentUtil.getName(modelRaw.getObject());
return createStringResource("PageResourceWizard.title.edit", name).getString();
}
};
}
private void initLayout() {
WizardModel wizardModel = new ResourceWizardModel(this);
if (!configurationOnly) {
wizardModel.add(new NameStep(modelRaw, this));
}
wizardModel.add(new ConfigurationStep(modelNoFetch, this));
if (!configurationOnly) {
wizardModel.add(new SchemaStep(modelFull, this));
wizardModel.add(new SchemaHandlingStep(modelFull, this));
wizardModel.add(new SynchronizationStep(modelFull, this));
wizardModel.add(new CapabilityStep(modelFull, this));
}
Wizard wizard = new Wizard(ID_WIZARD, new Model<>(wizardModel), issuesModel);
wizard.setOutputMarkupId(true);
add(wizard);
}
public void refreshIssues(@Nullable AjaxRequestTarget target) {
issuesModel.reset();
if (target != null) {
Wizard wizard = (Wizard) get(ID_WIZARD);
target.add(wizard.getIssuesPanel());
target.add(wizard.getSteps());
target.add(wizard.getButtons());
}
}
@NotNull public ResourceWizardIssuesModel getIssuesModel() {
return issuesModel;
}
public void resetModels() {
LOGGER.info("Resetting models");
modelRaw.reset();
modelNoFetch.reset();
modelFull.reset();
for (LoadableModel<?> model : dependentModels) {
model.reset();
}
}
public void registerDependentModel(@NotNull LoadableModel<?> model) {
dependentModels.add(model);
}
// questionable
public boolean isNewResource() {
return editedResourceOid == null;
}
public ObjectDelta<ResourceType> computeDiff(PrismObject<ResourceType> oldResource, PrismObject<ResourceType> newResource) {
ObjectDelta<ResourceType> delta = oldResource.diff(newResource);
if (!delta.isModify()) {
return delta;
}
final ItemPath RESULT_PATH = new ItemPath(ResourceType.F_FETCH_RESULT);
@SuppressWarnings("unchecked")
Iterator<? extends ItemDelta<?,?>> iterator = delta.getModifications().iterator();
while (iterator.hasNext()) {
ItemDelta<?,?> itemDelta = iterator.next();
if (RESULT_PATH.equivalent(itemDelta.getPath())) {
iterator.remove();
}
}
return delta;
}
// TODO change to debug
public void logDelta(ObjectDelta delta) {
LOGGER.info("Applying delta:\n{}", delta.debugDump());
}
public boolean isCurrentStepComplete() {
Wizard wizard = (Wizard) get(ID_WIZARD);
WizardStep activeStep = (WizardStep) wizard.getModelObject().getActiveStep();
return activeStep == null || activeStep.isComplete();
}
public Wizard getWizard() {
return (Wizard) get(ID_WIZARD);
}
public boolean isConfigurationOnly() {
return configurationOnly;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public void addEditingEnabledBehavior(Component... components) {
for (Component component : components) {
component.add(new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return !readOnly;
}
});
}
}
public void addEditingVisibleBehavior(Component... components) {
for (Component component : components) {
component.add(new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return !readOnly;
}
});
}
}
public NonEmptyModel<Boolean> getReadOnlyModel() {
return new NonEmptyModel<Boolean>() {
@NotNull
@Override
public Boolean getObject() {
return readOnly;
}
@Override
public void setObject(@NotNull Boolean object) {
throw new UnsupportedOperationException();
}
@Override
public void detach() {
}
};
}
public void visualize(AjaxRequestTarget target) {
setResponsePage(new PageResourceVisualization(modelFull.getObject()));
}
public boolean showSaveResultInPage(boolean saved, OperationResult result) {
return saved || WebComponentUtil.showResultInPage(result);
}
}