/* (c) 2016 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geogig.geoserver.web.repository; import static org.geogig.geoserver.config.RepositoryManager.isGeogigDirectory; import java.io.File; import java.io.Serializable; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponentPanel; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.validation.FormComponentFeedbackBorder; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.geogig.geoserver.config.ImportRepositoryFormBean; import org.geogig.geoserver.config.PostgresConfigBean; import org.geogig.geoserver.config.RepositoryInfo; import org.geogig.geoserver.config.RepositoryManager; import org.geogig.geoserver.model.DropDownModel; import org.geogig.geoserver.model.ImportRepositoryFormModel; import org.geogig.geoserver.util.PostgresConnectionErrorHandler; import org.geoserver.web.data.store.panel.DropDownChoiceParamPanel; import org.geoserver.web.data.store.panel.TextParamPanel; import org.locationtech.geogig.repository.RepositoryResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Wicket panel that holds the components for importing an existing GeoGig repository. This is * very similar to {@link RepositoryEditPanel}, however there are some subtle differences. Since * this panel is used for importing an existing repo, the validation checks are different. Also, the * {@link DirectoryChooser} is configured to allow repository directories to be selected instead of * just the parent directory. As such, the repository name is not separately selected. The * differences also called for a slightly different way of wrapping the {@link RepositoryInfo} data * bean with a different data model, {@link ImportRepositoryFormModel} and * {@link ImportRepositoryFormBean}. */ public class RepositoryImportPanel extends FormComponentPanel<RepositoryInfo> { private static final long serialVersionUID = 1L; private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryImportPanel.class); private final TextParamPanel repoNamePanel; private final DropDownChoiceParamPanel dropdownPanel; private final RepoDirectoryComponent repoDirectoryComponent; private final PostgresConfigFormPanel pgPanel; private final WebMarkupContainer settingsPanel; public RepositoryImportPanel(String id, IModel<RepositoryInfo> model) { super(id, model); setOutputMarkupId(true); // build the backing form model ImportRepositoryFormModel formModel = new ImportRepositoryFormModel(model); // add the dropdown to switch between configurations dropdownPanel = new DropDownChoiceParamPanel("configChoicePanel", new DropDownModel(model), new ResourceModel("RepositoryImportPanel.repositoryType", "Repository Type"), DropDownModel.CONFIG_LIST, true); final DropDownChoice<Serializable> dropDownChoice = dropdownPanel.getFormComponent(); dropDownChoice.add(new AjaxFormComponentUpdatingBehavior("change") { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { final String value = dropDownChoice.getModelObject().toString(); repoDirectoryComponent.setVisible(DropDownModel.DIRECTORY_CONFIG.equals(value)); pgPanel.setVisible(DropDownModel.PG_CONFIG.equals(value)); repoNamePanel.setVisible(DropDownModel.PG_CONFIG.equals(value)); target.add(settingsPanel); } }); add(dropdownPanel); settingsPanel = new WebMarkupContainer("settingsContainer"); settingsPanel.setOutputMarkupId(true); add(settingsPanel); repoNamePanel = new TextParamPanel("repositoryNamePanel", new PropertyModel(formModel, "repoName"), new ResourceModel("RepositoryImportPanel.repositoryName", "Repository Name"), true); repoNamePanel.setOutputMarkupId(true); repoNamePanel.getFormComponent().setOutputMarkupId(true); repoNamePanel.setVisible(DropDownModel.PG_CONFIG.equals(dropDownChoice.getModelObject() .toString())); settingsPanel.add(repoNamePanel); pgPanel = new PostgresConfigFormPanel("pgPanel", new PropertyModel<>(formModel, "pgBean")); pgPanel.setVisible(DropDownModel.PG_CONFIG .equals(dropDownChoice.getModelObject().toString())); settingsPanel.add(pgPanel); repoDirectoryComponent = new RepoDirectoryComponent("repoDirectoryPanel", formModel); repoDirectoryComponent.setOutputMarkupId(true); repoDirectoryComponent .setVisible(DropDownModel.DIRECTORY_CONFIG.equals(dropDownChoice.getModelObject() .toString())); settingsPanel.add(repoDirectoryComponent); add(new IValidator<RepositoryInfo>() { private static final long serialVersionUID = 1L; @Override public void validate(IValidatable<RepositoryInfo> validatable) { ValidationError error = new ValidationError(); RepositoryInfo repo = validatable.getValue(); final URI location = repo.getLocation(); // look for already configured repos List<RepositoryInfo> existingRepos = RepositoryManager.get().getAll(); for (RepositoryInfo configuredRepo : existingRepos) { if (configuredRepo.getLocation().equals(location)) { error.addKey("errRepositoryAlreadyConfigured"); // quit looking break; } } if (error.getKeys().isEmpty()) { final RepositoryResolver resolver = RepositoryResolver.lookup(location); final String scheme = location.getScheme(); if ("file".equals(scheme)) { File repoDir = new File(location); if (!repoDir.exists() || !repoDir.isDirectory()) { error.addKey("errRepositoryDirectoryDoesntExist"); } if (!isGeogigDirectory(repoDir)) { error.addKey("notAGeogigRepository"); } } else if ("postgresql".equals(scheme)) { try { if (!resolver.repoExists(location)) { error.addKey("errRepositoryDoesntExist"); } } catch (Exception ex) { // likely failed to connect LOGGER.error("Failed to connect to PostgreSQL database", ex); error.addKey("errCannotConnectToDatabase"); // find root cause error.setVariable("message", PostgresConnectionErrorHandler.getMessage( ex)); } } } if (!error.getKeys().isEmpty()) { validatable.error(error); } } }); } @Override public void convertInput() { RepositoryInfo repoInfo = getModelObject(); final String repoTypeChoice = dropdownPanel.getFormComponent().getConvertedInput() .toString(); if (null != repoTypeChoice) { switch (repoTypeChoice) { case DropDownModel.PG_CONFIG: // PG config used PostgresConfigBean bean = pgPanel.getConvertedInput(); // build a URI out of the config URI uri = bean.buildUriForRepo(repoNamePanel.getFormComponent() .getConvertedInput().toString().trim()); repoInfo.setLocation(uri); break; case DropDownModel.DIRECTORY_CONFIG: // local directory used ImportRepositoryFormBean formBean = repoDirectoryComponent.getConvertedInput(); Path uriPath = Paths.get(formBean.getRepoDirectory()); repoInfo.setLocation(uriPath.toUri()); break; default: throw new IllegalStateException( String.format("Unknown repositry type '%s', expected one of %s, %s", repoTypeChoice, DropDownModel.PG_CONFIG, DropDownModel.DIRECTORY_CONFIG)); } } setConvertedInput(repoInfo); } static class RepoDirectoryComponent extends FormComponentPanel<ImportRepositoryFormBean> { private static final long serialVersionUID = 1L; private final TextField<String> repoDirectoryField; private final ModalWindow dialog; RepoDirectoryComponent(String id, IModel<ImportRepositoryFormBean> model) { super(id, model); dialog = new ModalWindow("dialog"); add(dialog); repoDirectoryField = new TextField<>("repoDirectory", new PropertyModel<String>(model, "repoDirectory")); repoDirectoryField.setRequired(true); repoDirectoryField.setOutputMarkupId(true); IModel<String> labelModel = new ResourceModel( "RepositoryImportPanel.directoryLabel", "Repository Directory") { private static final long serialVersionUID = 1L; @Override public String getObject() { String value = super.getObject(); return value + " *"; } }; Label directoryLabel = new Label("repoLabel", labelModel.getObject()); add(directoryLabel); repoDirectoryField.setLabel(labelModel); FormComponentFeedbackBorder feedback = new FormComponentFeedbackBorder("wrapper"); feedback.add(repoDirectoryField); feedback.add(createChooserButton()); add(feedback); } @Override public void convertInput() { ImportRepositoryFormBean bean = new ImportRepositoryFormBean(); String repoDir = repoDirectoryField.getConvertedInput(); bean.setRepoDirectory(repoDir); setConvertedInput(bean); } private Component createChooserButton() { return new AjaxSubmitLink("chooser") { private static final long serialVersionUID = 1L; @Override public boolean getDefaultFormProcessing() { return false; } @Override protected void onSubmit(AjaxRequestTarget target, Form form) { File repoDirFile = null; repoDirectoryField.processInput(); String repoDir = repoDirectoryField.getConvertedInput(); if (repoDir != null && !repoDir.trim().isEmpty()) { repoDirFile = new File(repoDir); } DirectoryChooser chooser = new DirectoryChooser(dialog.getContentId(), new Model<>(repoDirFile), true) { private static final long serialVersionUID = 1L; @Override protected void geogigDirectoryClicked(File file, AjaxRequestTarget target) { repoDirectoryField.clearInput(); repoDirectoryField.setModelObject(file.getAbsolutePath()); target.add(repoDirectoryField); dialog.close(target); } @Override protected void directorySelected(File file, AjaxRequestTarget target) { repoDirectoryField.clearInput(); repoDirectoryField.setModelObject(file.getAbsolutePath()); target.add(repoDirectoryField); dialog.close(target); } }; chooser.setFileTableHeight(null); dialog.setContent(chooser); dialog.setTitle(new ResourceModel( "GeoGigDirectoryFormComponent.chooser.chooseParentTile")); dialog.show(target); } }; } } }