/* * RHQ Management Platform * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.alert.definitions; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.types.TitleOrientation; import com.smartgwt.client.widgets.form.DynamicForm; import com.smartgwt.client.widgets.form.events.FormSubmitFailedEvent; import com.smartgwt.client.widgets.form.events.FormSubmitFailedHandler; import com.smartgwt.client.widgets.form.events.ItemChangeEvent; import com.smartgwt.client.widgets.form.events.ItemChangeHandler; import com.smartgwt.client.widgets.form.fields.ButtonItem; import com.smartgwt.client.widgets.form.fields.FormItem; import com.smartgwt.client.widgets.form.fields.FormItemIcon; import com.smartgwt.client.widgets.form.fields.PasswordItem; import com.smartgwt.client.widgets.form.fields.SectionItem; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.TextItem; import com.smartgwt.client.widgets.form.fields.UploadItem; import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; import com.smartgwt.client.widgets.form.fields.events.ClickEvent; import com.smartgwt.client.widgets.form.fields.events.ClickHandler; import org.rhq.core.domain.alert.notification.AlertNotification; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.content.PackageVersionFormatDescription; import org.rhq.core.domain.content.Repo; import org.rhq.core.domain.content.composite.PackageAndLatestVersionComposite; import org.rhq.core.domain.content.composite.PackageTypeAndVersionFormatComposite; import org.rhq.core.domain.criteria.PackageCriteria; import org.rhq.core.domain.criteria.RepoCriteria; import org.rhq.core.domain.criteria.SubjectCriteria; import org.rhq.core.domain.util.OSGiVersion; import org.rhq.core.domain.util.PageList; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.ImageManager; import org.rhq.coregui.client.UserSessionManager; import org.rhq.coregui.client.components.form.RadioGroupWithComponentsItem; import org.rhq.coregui.client.components.form.SortedSelectItem; import org.rhq.coregui.client.components.upload.DynamicFormHandler; import org.rhq.coregui.client.components.upload.DynamicFormSubmitCompleteEvent; import org.rhq.coregui.client.components.upload.PackageVersionFileUploadForm; import org.rhq.coregui.client.gwt.GWTServiceLookup; /** * A form to configure the CLI script alert notification. * * @author Lukas Krejci */ public class CliNotificationSenderForm extends AbstractNotificationSenderForm { private static final String PROP_PACKAGE_ID = "packageId"; private static final String PROP_REPO_ID = "repoId"; private static final String PROP_USER_ID = "userId"; private static final String PROP_USER_NAME = "userName"; private static final String PROP_USER_PASSWORD = "userPassword"; private static final String PACKAGE_TYPE_NAME = "org.rhq.enterprise.server.plugins.packagetypeCli.SERVER_SIDE_CLI_SCRIPT"; private static class Config { List<Repo> allRepos; Repo selectedRepo; List<PackageAndLatestVersionComposite> allPackages; PackageAndLatestVersionComposite selectedPackage; Subject selectedSubject; /* * This is a counter to keep track if all the async * loading of the above data has finished. * * In principle, this should be AtomicInteger but GWT didn't like it. * * The reason why it still works as a normal integer even though it * is being updated from within the AsyncCallbacks is that the javascript * engines in the browsers are single threaded and therefore, even though * the data is loaded in the background, it is processed (and this variable * updated) only in that single thread. */ int __handlerCounter = 3; public void setSelectedRepo(int repoId) { if (allRepos != null) { for (Repo r : allRepos) { if (r.getId() == repoId) { selectedRepo = r; break; } } } } public void setSelectedPackage(int packageId) { if (allPackages != null) { for (PackageAndLatestVersionComposite p : allPackages) { if (p.getGeneralPackage().getId() == packageId) { selectedPackage = p; break; } } } } } private static class PackageVersionFileUploadFormWithVersion extends PackageVersionFileUploadForm { public PackageVersionFileUploadFormWithVersion(int packageTypeId) { super(packageTypeId, null, null, null, null, true, false, false); setName("File"); } protected List<FormItem> getOnDrawItems() { List<FormItem> items = super.getOnDrawItems(); TextItem version = new TextItem("editableVersion", MSG.view_alert_definition_notification_cliScript_editor_newScriptVersion()); version.setColSpan(getNumCols()); version.addChangedHandler(new ChangedHandler() { public void onChanged(ChangedEvent event) { getField("version").setValue(event.getValue()); } }); items.add(version); return items; } } private static abstract class ForwardingDynamicFormHandler implements DynamicFormHandler { private AsyncCallback<Void> callback; public AsyncCallback<Void> getCallback() { return callback; } public void setCallback(AsyncCallback<Void> callback) { this.callback = callback; } }; private static abstract class ForwardingSubmitFailedHandler implements FormSubmitFailedHandler { private AsyncCallback<Void> callback; public AsyncCallback<Void> getCallback() { return callback; } public void setCallback(AsyncCallback<Void> callback) { this.callback = callback; } }; private static String FAILED_LAST_TIME = "failed"; private boolean formBuilt; private SelectItem repoSelector; private RadioGroupWithComponentsItem packageSelector; private SelectItem existingPackageSelector; private RadioGroupWithComponentsItem userSelector; private PackageVersionFileUploadFormWithVersion uploadForm; private PackageTypeAndVersionFormatComposite cliScriptPackageType; private TextItem anotherUserName; private TextItem anotherUserPassword; private ButtonItem verifyUserButton; private FormItemIcon successIcon; private FormItemIcon failureIcon; private ForwardingSubmitFailedHandler uploadFailureHandler; private ForwardingDynamicFormHandler uploadSuccessHandler; private Config config; public CliNotificationSenderForm(AlertNotification notif, String sender) { super(notif, sender); } @Override protected void onInit() { super.onInit(); if (!formBuilt) { loadPackageType(new AsyncCallback<PackageTypeAndVersionFormatComposite>() { public void onFailure(Throwable t) { CoreGUI.getErrorHandler().handleError( MSG.view_alert_definition_notification_cliScript_editor_loadFailed(), t); } public void onSuccess(PackageTypeAndVersionFormatComposite result) { cliScriptPackageType = result; DynamicForm form = new DynamicForm(); form.setTitleOrientation(TitleOrientation.TOP); form.setWidth(500); SectionItem repoSection = new SectionItem("repoSection"); repoSection.setDefaultValue(MSG.view_alert_definition_notification_cliScript_editor_repository()); repoSection.setWidth("100%"); SectionItem packageSection = new SectionItem("packageSection"); packageSection.setDefaultValue(MSG.view_alert_definition_notification_cliScript_editor_script()); SectionItem userSection = new SectionItem("userSection"); userSection.setDefaultValue(MSG.view_alert_definition_notification_cliScript_editor_whichUser()); repoSelector = new SortedSelectItem("repoSelector", MSG.view_alert_definition_notification_cliScript_editor_selectRepo()); repoSelector.setDefaultToFirstOption(true); repoSelector.setWrapTitle(false); repoSelector.setWidth(400); repoSelector.setValueMap(MSG.common_msg_loading()); repoSelector.setDisabled(true); LinkedHashMap<String, DynamicForm> packageSelectItems = new LinkedHashMap<String, DynamicForm>(); packageSelectItems.put(MSG.view_alert_definition_notification_cliScript_editor_existingScript(), createExistingPackageForm()); packageSelectItems.put(MSG.view_alert_definition_notification_cliScript_editor_uploadNewScript(), createUploadNewScriptForm()); packageSelector = new RadioGroupWithComponentsItem("packageSelector", "", packageSelectItems, form); packageSelector.setWidth("100%"); LinkedHashMap<String, DynamicForm> userSelectItems = new LinkedHashMap<String, DynamicForm>(); userSelectItems.put(MSG.view_alert_definition_notification_cliScript_editor_thisUser(), null); userSelectItems.put(MSG.view_alert_definition_notification_cliScript_editor_anotherUser(), createAnotherUserForm()); userSelector = new RadioGroupWithComponentsItem("userSelector", "", userSelectItems, form); userSelector.setWidth("100%"); userSelector.setShowTitle(false); repoSection.setItemIds("repoSelector"); packageSection.setItemIds("packageSelector"); userSection.setItemIds("userSelector"); form.setFields(userSection, userSelector, repoSection, repoSelector, packageSection, packageSelector); addMember(form); loadConfig(new AsyncCallback<Config>() { public void onFailure(Throwable t) { CoreGUI.getErrorHandler().handleError( MSG.view_alert_definition_notification_cliScript_editor_loadFailed(), t); } public void onSuccess(Config config) { CliNotificationSenderForm.this.config = config; setupRepoSelector(); setupPackageSelector(); setupUserSelector(); formBuilt = true; markForRedraw(); } }); } }); } } private void setupUserSelector() { if (config.selectedSubject != null && !UserSessionManager.getSessionSubject().equals(config.selectedSubject)) { userSelector.setSelected(MSG.view_alert_definition_notification_cliScript_editor_anotherUser()); DynamicForm anotherUserForm = (DynamicForm) userSelector.getSelectedComponent(); anotherUserForm.getItem("userName").setValue(config.selectedSubject.getName()); } else { userSelector.setSelected(MSG.view_alert_definition_notification_cliScript_editor_thisUser()); } markForRedraw(); } private void setupPackageSelector() { LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(); for (PackageAndLatestVersionComposite p : config.allPackages) { String label = p.getGeneralPackage().getName() + " (" + p.getLatestPackageVersion().getVersion() + ")"; map.put(String.valueOf(p.getGeneralPackage().getId()), label); } existingPackageSelector.setValueMap(map); if (config.selectedPackage != null) { existingPackageSelector.setValue(config.selectedPackage.getGeneralPackage().getId()); packageSelector.setSelected(MSG.view_alert_definition_notification_cliScript_editor_existingScript()); } else { packageSelector.setSelected(MSG.view_alert_definition_notification_cliScript_editor_uploadNewScript()); } if (!formBuilt) { existingPackageSelector.addChangedHandler(new ChangedHandler() { public void onChanged(ChangedEvent event) { int packageId = Integer.valueOf(event.getItem().getValue().toString()); config.setSelectedPackage(packageId); } }); } existingPackageSelector.setDisabled(false); markForRedraw(); } private void setupRepoSelector() { LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(); for (Repo r : config.allRepos) { map.put(String.valueOf(r.getId()), r.getName()); } repoSelector.setValueMap(map); if (config.selectedRepo != null) { repoSelector.setValue(config.selectedRepo.getId()); packageSelector.setDisabled(false); uploadForm.setRepoId(config.selectedRepo.getId()); } else { repoSelector.setValue(""); packageSelector.setDisabled(true); uploadForm.setRepoId(null); } if (!formBuilt) { repoSelector.addChangedHandler(new ChangedHandler() { public void onChanged(ChangedEvent event) { final Integer repoId = Integer.valueOf(event.getItem().getValue().toString()); config.setSelectedRepo(repoId); PackageCriteria pc = new PackageCriteria(); pc.addFilterRepoId(repoId); pc.addFilterPackageTypeId(cliScriptPackageType.getPackageType().getId()); packageSelector.setDisabled(false); existingPackageSelector.setDisabled(true); existingPackageSelector.setValueMap(MSG.common_msg_loading()); GWTServiceLookup.getContentService().findPackagesWithLatestVersion(pc, new AsyncCallback<PageList<PackageAndLatestVersionComposite>>() { public void onSuccess(PageList<PackageAndLatestVersionComposite> result) { config.allPackages = result; config.selectedPackage = result.isEmpty() ? null : result.get(0); //we're autoselecting the first item setupPackageSelector(); uploadForm.setRepoId(repoId); } public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError( MSG.view_alert_definition_notification_cliScript_editor_loadFailed(), caught); } }); } }); } repoSelector.setDisabled(false); markForRedraw(); } @Override public void validate(final AsyncCallback<Void> callback) { if (userSelector.getSelectedIndex() == 0) { getConfiguration().put(new PropertySimple(PROP_USER_ID, UserSessionManager.getSessionSubject().getId())); validatePackage(callback); } else { checkAuthenticationAndDo(new AsyncCallback<Void>() { public void onFailure(Throwable caught) { callback.onFailure(caught); } public void onSuccess(Void result) { getConfiguration().put(new PropertySimple(PROP_USER_ID, config.selectedSubject.getId())); getConfiguration().put(new PropertySimple(PROP_USER_NAME, anotherUserName.getEnteredValue())); getConfiguration().put( new PropertySimple(PROP_USER_PASSWORD, anotherUserPassword.getEnteredValue())); validatePackage(callback); } }); } } private void validatePackage(final AsyncCallback<Void> callback) { if (config.selectedRepo == null) { setFailed(repoSelector); callback.onFailure(null); return; } setOk(repoSelector); if (!existingPackageSelector.isDisabled()) { if (config.selectedPackage == null) { setFailed(existingPackageSelector); callback.onFailure(null); return; } setOk(existingPackageSelector); } getConfiguration().put(new PropertySimple(PROP_REPO_ID, config.selectedRepo.getId())); if (packageSelector.getSelectedIndex() == 0) { getConfiguration().put( new PropertySimple(PROP_PACKAGE_ID, config.selectedPackage.getGeneralPackage().getId())); callback.onSuccess(null); } else { if (cliScriptPackageType.getVersionFormat() != null) { String versionRegex = cliScriptPackageType.getVersionFormat().getFullFormatRegex(); if (versionRegex != null) { Object version = uploadForm.getField("version").getValue(); if (version == null || !matches(version.toString(), versionRegex)) { uploadForm.getItem("editableVersion").setIcons(failureIcon); callback.onFailure(null); return; } else { uploadForm.getItem("editableVersion").setIcons(successIcon); } } else { uploadForm.getItem("editableVersion").setIcons(); } } uploadFailureHandler.setCallback(callback); uploadSuccessHandler.setCallback(new AsyncCallback<Void>() { public void onFailure(Throwable caught) { callback.onFailure(null); }; public void onSuccess(Void result) { getConfiguration().put(new PropertySimple(PROP_PACKAGE_ID, uploadForm.getPackageId())); callback.onSuccess(null); }; }); uploadForm.submitForm(); } } private void loadConfig(final AsyncCallback<Config> handler) { final String repoId = getConfiguration().getSimpleValue(PROP_REPO_ID, null); final String packageId = getConfiguration().getSimpleValue(PROP_PACKAGE_ID, null); final String subjectId = getConfiguration().getSimpleValue(PROP_USER_ID, null); final Config config = new Config(); RepoCriteria rc = new RepoCriteria(); GWTServiceLookup.getRepoService().findReposByCriteria(rc, new AsyncCallback<PageList<Repo>>() { public void onSuccess(PageList<Repo> result) { config.allRepos = result; if (repoId != null && repoId.trim().length() > 0) { final int rid = Integer.parseInt(repoId); config.setSelectedRepo(rid); } if (--config.__handlerCounter == 0) { handler.onSuccess(config); } } public void onFailure(Throwable caught) { handler.onFailure(caught); } }); if (repoId != null && repoId.trim().length() > 0) { PackageCriteria pc = new PackageCriteria(); pc.addFilterRepoId(Integer.parseInt(repoId)); pc.addFilterPackageTypeId(cliScriptPackageType.getPackageType().getId()); GWTServiceLookup.getContentService().findPackagesWithLatestVersion(pc, new AsyncCallback<PageList<PackageAndLatestVersionComposite>>() { public void onSuccess(PageList<PackageAndLatestVersionComposite> result) { config.allPackages = result; if (packageId != null && packageId.trim().length() > 0) { int pid = Integer.parseInt(packageId); config.setSelectedPackage(pid); } if (--config.__handlerCounter == 0) { handler.onSuccess(config); } } public void onFailure(Throwable caught) { handler.onFailure(caught); } }); } else { config.allPackages = Collections.emptyList(); --config.__handlerCounter; } if (subjectId != null && subjectId.trim().length() > 0) { int sid = Integer.parseInt(subjectId); SubjectCriteria c = new SubjectCriteria(); c.addFilterId(sid); GWTServiceLookup.getSubjectService().findSubjectsByCriteria(c, new AsyncCallback<PageList<Subject>>() { public void onSuccess(PageList<Subject> result) { if (result.size() > 0) { config.selectedSubject = result.get(0); } if (--config.__handlerCounter == 0) { handler.onSuccess(config); } } public void onFailure(Throwable caught) { handler.onFailure(caught); } }); } else { --config.__handlerCounter; } } private DynamicForm createAnotherUserForm() { DynamicForm form = new DynamicForm(); form.setTitleOrientation(TitleOrientation.TOP); anotherUserName = new TextItem("userName", MSG.common_title_username()); anotherUserPassword = new PasswordItem("password", MSG.common_title_password()); anotherUserPassword.setAttribute("autocomplete", "off"); verifyUserButton = new ButtonItem("verify", MSG.view_alert_definition_notification_cliScript_editor_verifyAuthentication()); successIcon = new FormItemIcon(); successIcon.setSrc(ImageManager.getAvailabilityIcon(Boolean.TRUE)); successIcon.setWidth(16); successIcon.setHeight(16); failureIcon = new FormItemIcon(); failureIcon.setSrc(ImageManager.getAvailabilityIcon(Boolean.FALSE)); failureIcon.setWidth(16); failureIcon.setHeight(16); form.setFields(anotherUserName, anotherUserPassword, verifyUserButton); verifyUserButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { //just checking the auth is ok, no other action is needed. checkAuthenticationAndDo(new AsyncCallback<Void>() { public void onFailure(Throwable caught) { } public void onSuccess(Void result) { } }); } }); return form; } private DynamicForm createExistingPackageForm() { DynamicForm form = new DynamicForm(); form.setTitleOrientation(TitleOrientation.TOP); existingPackageSelector = new SortedSelectItem("existingPackageSelector", ""); existingPackageSelector.setDefaultToFirstOption(true); existingPackageSelector.setWrapTitle(false); existingPackageSelector.setRedrawOnChange(true); existingPackageSelector.setWidth(300); existingPackageSelector.setValueMap(MSG.common_msg_loading()); existingPackageSelector.setDisabled(true); form.setFields(existingPackageSelector); return form; } private DynamicForm createUploadNewScriptForm() { uploadForm = new PackageVersionFileUploadFormWithVersion(cliScriptPackageType.getPackageType() .getId()); uploadForm.setTitleOrientation(TitleOrientation.TOP); uploadForm.setPackageTypeId(cliScriptPackageType.getPackageType().getId()); uploadFailureHandler = (new ForwardingSubmitFailedHandler() { public void onFormSubmitFailed(FormSubmitFailedEvent event) { if (getCallback() != null) { getCallback().onFailure(null); } } }); uploadSuccessHandler = new ForwardingDynamicFormHandler() { public void onSubmitComplete(DynamicFormSubmitCompleteEvent event) { if (uploadForm.getPackageId() == 0 || uploadForm.getPackageVersionId() == 0) { if (getCallback() != null) { getCallback().onFailure(null); } return; } if (getCallback() != null) { getCallback().onSuccess(null); } } }; uploadForm.addFormHandler(uploadSuccessHandler); uploadForm.addItemChangeHandler(new ItemChangeHandler() { public void onItemChange(ItemChangeEvent event) { if (event.getItem() instanceof UploadItem) { if (event.getNewValue() == null) { uploadForm.getField("editableVersion").setValue(""); uploadForm.getField("version").setValue(""); return; } String fileName = event.getNewValue().toString(); if (config.allPackages != null) { for (PackageAndLatestVersionComposite plv : config.allPackages) { if (plv.getGeneralPackage().getName().equals(fileName)) { try { String version = plv.getLatestPackageVersion().getVersion(); PackageVersionFormatDescription format = cliScriptPackageType.getVersionFormat(); if (format == null) { return; } if (format.getOsgiVersionExtractionRegex() == null) { return; } String regex = format.getOsgiVersionExtractionRegex(); int group = format.getOsgiVersionGroupIndex(); String oldOsgiVersion = getOsgiVersion(version, regex, group); OSGiVersion v = new OSGiVersion(oldOsgiVersion); if (v.getMicroIfDefined() != null) { v.setMicro(v.getMicro() + 1); } else if (v.getMinorIfDefined() != null) { v.setMinor(v.getMinor() + 1); } else { v.setMajor(v.getMajor() + 1); } String newVersion = version.replace(oldOsgiVersion, v.toString()); uploadForm.getField("editableVersion").setValue(newVersion); uploadForm.getField("version").setValue(newVersion); return; } catch (Exception e) { //ok, can't suggest anything return; } } } //no existing package exists with the same name as the file being uploaded String defaultVersion = ""; if (cliScriptPackageType.getVersionFormat() != null && cliScriptPackageType.getVersionFormat().getOsgiVersionExtractionRegex() != null) { //check if the package format understands plain OSGi versioning if (matches("1.0", cliScriptPackageType.getVersionFormat().getFullFormatRegex())) { defaultVersion = "1.0"; } } uploadForm.getField("editableVersion").setValue(defaultVersion); uploadForm.getField("version").setValue(defaultVersion); } } } }); return uploadForm; } private void loadPackageType(AsyncCallback<PackageTypeAndVersionFormatComposite> handler) { GWTServiceLookup.getContentService().findPackageType(null, PACKAGE_TYPE_NAME, handler); } private void setFailed(FormItem item) { item.setIcons(failureIcon); item.setAttribute(FAILED_LAST_TIME, true); } private void setOk(FormItem item) { Boolean lastTimeFail = item.getAttributeAsBoolean(FAILED_LAST_TIME); if (lastTimeFail != null && lastTimeFail) { item.setIcons(successIcon); item.setAttribute(FAILED_LAST_TIME, false); } } private void checkAuthenticationAndDo(final AsyncCallback<Void> action) { String username = anotherUserName.getEnteredValue(); String password = anotherUserPassword.getEnteredValue(); GWTServiceLookup.getSubjectService().checkAuthentication(username, password, new AsyncCallback<Subject>() { public void onFailure(Throwable caught) { config.selectedSubject = null; verifyUserButton.setIcons(failureIcon); markForRedraw(); action.onFailure(caught); } public void onSuccess(Subject result) { config.selectedSubject = result; verifyUserButton.setIcons(result == null ? failureIcon : successIcon); markForRedraw(); if (result == null) { action.onFailure(null); } else { action.onSuccess(null); } } }); } private static native String getOsgiVersion(String version, String osgiRegex, int groupIdx) /*-{ var re = new RegExp(osgiRegex); var groupRef = '$' + groupIdx; version = version.replace(re, groupRef); return version; }-*/; private static native boolean matches(String string, String regex) /*-{ var re = new RegExp(regex); return re.test(string); }-*/; }