001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.apps.scheduler; 029 030import org.opencms.file.CmsResourceFilter; 031import org.opencms.main.CmsIllegalArgumentException; 032import org.opencms.main.CmsLog; 033import org.opencms.main.CmsRuntimeException; 034import org.opencms.monitor.CmsMemoryMonitor; 035import org.opencms.notification.CmsContentNotificationJob; 036import org.opencms.relations.CmsExternalLinksValidator; 037import org.opencms.relations.CmsInternalRelationsValidationJob; 038import org.opencms.scheduler.CmsScheduledJobInfo; 039import org.opencms.scheduler.jobs.CmsCreateImageSizeJob; 040import org.opencms.scheduler.jobs.CmsDeleteExpiredResourcesJob; 041import org.opencms.scheduler.jobs.CmsHistoryClearJob; 042import org.opencms.scheduler.jobs.CmsImageCacheCleanupJob; 043import org.opencms.scheduler.jobs.CmsPublishJob; 044import org.opencms.scheduler.jobs.CmsStaticExportJob; 045import org.opencms.scheduler.jobs.CmsUnsubscribeDeletedResourcesJob; 046import org.opencms.search.CmsSearchManager; 047import org.opencms.ui.A_CmsUI; 048import org.opencms.ui.CmsVaadinUtils; 049import org.opencms.ui.Messages; 050import org.opencms.ui.components.CmsBasicDialog; 051import org.opencms.ui.components.OpenCmsTheme; 052import org.opencms.ui.components.fileselect.CmsPathSelectField; 053import org.opencms.ui.util.CmsComboNullToEmptyConverter; 054import org.opencms.ui.util.CmsNullToEmptyConverter; 055import org.opencms.util.CmsStringUtil; 056 057import java.util.Collections; 058import java.util.Map; 059import java.util.SortedMap; 060import java.util.TreeMap; 061 062import org.apache.commons.logging.Log; 063 064import com.vaadin.server.FontAwesome; 065import com.vaadin.ui.Button; 066import com.vaadin.ui.Button.ClickEvent; 067import com.vaadin.ui.Button.ClickListener; 068import com.vaadin.ui.Component; 069import com.vaadin.ui.FormLayout; 070import com.vaadin.ui.themes.ValoTheme; 071import com.vaadin.v7.data.Validator; 072import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; 073import com.vaadin.v7.shared.ui.combobox.FilteringMode; 074import com.vaadin.v7.ui.AbstractField; 075import com.vaadin.v7.ui.CheckBox; 076import com.vaadin.v7.ui.ComboBox; 077import com.vaadin.v7.ui.HorizontalLayout; 078import com.vaadin.v7.ui.TextField; 079 080/** 081 * Form used to edit a scheduled job.<p> 082 */ 083public class CmsJobEditView extends CmsBasicDialog { 084 085 /** 086 * Validator for the cron expression field.<p> 087 */ 088 public class CronExpressionValidator implements Validator { 089 090 /** Serial version id. */ 091 private static final long serialVersionUID = 1L; 092 093 /** 094 * @see com.vaadin.data.Validator#validate(java.lang.Object) 095 */ 096 public void validate(Object value) throws InvalidValueException { 097 098 CmsScheduledJobInfo info = new CmsScheduledJobInfo(); 099 // Job name may be needed in exception 100 try { 101 info.setJobName(m_fieldJobName.getValue()); 102 } catch (CmsRuntimeException e) { 103 throw new InvalidValueException(e.getLocalizedMessage(A_CmsUI.get().getLocale())); 104 } 105 String stringValue = (String)value; 106 try { 107 info.setCronExpression(stringValue); 108 } catch (CmsIllegalArgumentException e) { 109 throw new InvalidValueException(e.getLocalizedMessage(A_CmsUI.get().getLocale())); 110 } 111 } 112 } 113 114 /** 115 * Validator for the Java class name field.<p> 116 */ 117 public class JobClassValidator implements Validator { 118 119 /** Serial version id. */ 120 private static final long serialVersionUID = 1L; 121 122 /** 123 * @see com.vaadin.data.Validator#validate(java.lang.Object) 124 */ 125 public void validate(Object value) throws InvalidValueException { 126 127 CmsScheduledJobInfo info = new CmsScheduledJobInfo(); 128 String stringValue = (String)value; 129 130 // Job name may be needed in exception 131 try { 132 info.setJobName(m_fieldJobName.getValue()); 133 } catch (CmsRuntimeException e) { 134 throw new InvalidValueException(e.getLocalizedMessage(A_CmsUI.get().getLocale())); 135 } 136 try { 137 info.setClassName(stringValue); 138 } catch (CmsIllegalArgumentException e) { 139 throw new InvalidValueException(e.getLocalizedMessage(A_CmsUI.get().getLocale())); 140 } 141 } 142 } 143 144 /** 145 * Validator for the job name.<p> 146 */ 147 public class JobNameValidator implements Validator { 148 149 /** Serial version id. */ 150 private static final long serialVersionUID = 1L; 151 152 /** 153 * @see com.vaadin.data.Validator#validate(java.lang.Object) 154 */ 155 public void validate(Object value) throws InvalidValueException { 156 157 CmsScheduledJobInfo info = new CmsScheduledJobInfo(); 158 String name = (String)value; 159 try { 160 info.setJobName(name); 161 } catch (CmsIllegalArgumentException e) { 162 throw new InvalidValueException(e.getLocalizedMessage(A_CmsUI.get().getLocale())); 163 164 } 165 } 166 } 167 168 /** 169 * TODO this is the same like CmsRemovableFormRow 170 * Widget used to display a line of text and also a remove button to remove the widget.<p> 171 */ 172 class ParamLine extends HorizontalLayout { 173 174 /** Serial version id. */ 175 private static final long serialVersionUID = 1L; 176 177 /** The text input field. */ 178 private TextField m_input; 179 180 /** 181 * Creates a new instance.<p> 182 * 183 * @param content the initial content of the text field 184 */ 185 public ParamLine(String content) { 186 187 setWidth("100%"); 188 TextField input = new TextField(); 189 m_input = input; 190 setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_PARAMETER_0)); 191 setSpacing(true); 192 input.setValue(content); 193 input.setWidth("100%"); 194 addComponent(input); 195 setExpandRatio(input, 1f); 196 Button deleteButton = new Button(""); 197 deleteButton.addStyleName(ValoTheme.BUTTON_LINK); 198 deleteButton.setIcon(FontAwesome.TIMES_CIRCLE); 199 deleteButton.addStyleName(OpenCmsTheme.BUTTON_UNPADDED); 200 deleteButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_REMOVE_PARAMETER_0)); 201 deleteButton.addClickListener(new ClickListener() { 202 203 private static final long serialVersionUID = 1L; 204 205 public void buttonClick(ClickEvent event) { 206 207 m_paramContainer.removeComponent(ParamLine.this); 208 209 } 210 }); 211 addComponent(deleteButton); 212 213 } 214 215 /** 216 * Gets the value of the text field.<p> 217 * 218 * @return the value of the text field 219 */ 220 public String getValue() { 221 222 return m_input.getValue(); 223 } 224 225 } 226 227 /** Logger instance for this class. */ 228 static final Log LOG = CmsLog.getLog(CmsJobEditView.class); 229 230 /** Serial version id. */ 231 private static final long serialVersionUID = 1L; 232 233 /** Field for the job name. */ 234 TextField m_fieldJobName; 235 236 /** Edited job. */ 237 CmsScheduledJobInfo m_job = new CmsScheduledJobInfo(); 238 239 /** The job manager instance. */ 240 CmsJobManagerApp m_manager; 241 242 /** Form containing the job parameters. */ 243 FormLayout m_paramContainer; 244 245 /** Button to add a new parameter. */ 246 private Button m_buttonAddParam; 247 248 /** The cancel button. */ 249 private Button m_cancel; 250 251 /** Field to activate / deactivate the job. */ 252 private CheckBox m_fieldActive; 253 254 /** Field for the cron expression. */ 255 private ComboBox m_fieldCron; 256 257 /** Field for the encoding. */ 258 private TextField m_fieldEncoding; 259 260 /** Field for the class name. */ 261 private ComboBox m_fieldJobClass; 262 263 /** Field for the locale. */ 264 private TextField m_fieldLocale; 265 266 /** Field for the project. */ 267 private TextField m_fieldProject; 268 269 /** Field for the remote address. */ 270 private TextField m_fieldRemoteAddress; 271 272 /** 'Reuse instance' check box. */ 273 private CheckBox m_fieldReuseInstance; 274 275 /** Field for the site root. */ 276 private CmsPathSelectField m_fieldSiteRoot; 277 278 /** Field for the URI. */ 279 private CmsPathSelectField m_fieldUri; 280 281 /** Field for the user name. */ 282 private TextField m_fieldUser; 283 284 /** Field group. */ 285 private BeanFieldGroup<CmsScheduledJobInfo> m_group; 286 287 /** The ok button. */ 288 private Button m_ok; 289 290 /** 291 * Creates a new instance.<p> 292 * 293 * @param manager the job manager instance 294 * @param job the job to be edited 295 */ 296 public CmsJobEditView(CmsJobManagerApp manager, CmsScheduledJobInfo job) { 297 298 m_manager = manager; 299 m_job = job; 300 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null); 301 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(job.getClassName())) { 302 displayResourceInfoDirectly( 303 Collections.singletonList(CmsJobTable.getJobInfo(job.getJobName(), job.getClassName()))); 304 } 305 m_ok.addClickListener(new ClickListener() { 306 307 private static final long serialVersionUID = 1L; 308 309 public void buttonClick(ClickEvent event) { 310 311 if (trySaveToBean()) { 312 m_manager.writeElement(m_job); 313 m_manager.closeDialogWindow(true); 314 } 315 316 } 317 }); 318 m_cancel.addClickListener(new ClickListener() { 319 320 private static final long serialVersionUID = 1L; 321 322 public void buttonClick(ClickEvent event) { 323 324 m_manager.closeDialogWindow(false); 325 } 326 }); 327 328 BeanFieldGroup<CmsScheduledJobInfo> group = new BeanFieldGroup<CmsScheduledJobInfo>(CmsScheduledJobInfo.class); 329 group.setItemDataSource(m_job); 330 m_group = group; 331 332 bindField(m_fieldJobName, "jobName"); 333 bindField(m_fieldJobClass, "className"); 334 bindField(m_fieldCron, "cronExpression"); 335 bindField(m_fieldReuseInstance, "reuseInstance"); 336 bindField(m_fieldActive, "active"); 337 bindField(m_fieldUser, "contextInfo.userName"); 338 bindField(m_fieldProject, "contextInfo.projectName"); 339 bindField(m_fieldSiteRoot, "contextInfo.siteRoot"); 340 bindField(m_fieldUri, "contextInfo.requestedUri"); 341 bindField(m_fieldLocale, "contextInfo.localeName"); 342 bindField(m_fieldEncoding, "contextInfo.encoding"); 343 bindField(m_fieldRemoteAddress, "contextInfo.remoteAddr"); 344 345 m_fieldJobName.setConverter(new CmsNullToEmptyConverter()); 346 m_fieldJobClass.setConverter(new CmsComboNullToEmptyConverter()); 347 m_fieldCron.setConverter(new CmsComboNullToEmptyConverter()); 348 349 m_buttonAddParam.addClickListener(new ClickListener() { 350 351 /** Serial version id. */ 352 private static final long serialVersionUID = 1L; 353 354 public void buttonClick(ClickEvent event) { 355 356 addParamLine(""); 357 } 358 }); 359 360 m_fieldJobName.addValidator(new JobNameValidator()); 361 m_fieldJobClass.setFilteringMode(FilteringMode.OFF); 362 m_fieldCron.setFilteringMode(FilteringMode.OFF); 363 m_fieldCron.setNewItemsAllowed(true); 364 m_fieldJobClass.setNewItemsAllowed(true); 365 m_fieldJobClass.setPageLength(20); 366 m_fieldJobClass.addValidator(new JobClassValidator()); 367 m_fieldCron.addValidator(new CronExpressionValidator()); 368 m_fieldJobClass.addItem( 369 370 CmsInternalRelationsValidationJob.class.getName()); 371 m_fieldJobClass.addItem(CmsPublishJob.class.getName()); 372 m_fieldJobClass.addItem(CmsStaticExportJob.class.getName()); 373 m_fieldJobClass.addItem(CmsExternalLinksValidator.class.getName()); 374 m_fieldJobClass.addItem(CmsMemoryMonitor.class.getName()); 375 m_fieldJobClass.addItem(CmsSearchManager.class.getName()); 376 m_fieldJobClass.addItem(CmsContentNotificationJob.class.getName()); 377 m_fieldJobClass.addItem(CmsCreateImageSizeJob.class.getName()); 378 m_fieldJobClass.addItem(CmsImageCacheCleanupJob.class.getName()); 379 m_fieldJobClass.addItem(CmsHistoryClearJob.class.getName()); 380 m_fieldJobClass.addItem(CmsDeleteExpiredResourcesJob.class.getName()); 381 m_fieldJobClass.addItem(CmsUnsubscribeDeletedResourcesJob.class.getName()); 382 383 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_job.getClassName())) { 384 m_fieldJobClass.addItem(m_job.getClassName()); 385 } 386 387 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_job.getCronExpression())) { 388 m_fieldCron.addItem(m_job.getCronExpression()); 389 } 390 391 for (String item : new String[] { 392 "0 0 3 * * ?", 393 "0 0/30 * * * ?", 394 "0 30 8 ? * 4", 395 "0 15 18 20 * ?", 396 "0 45 15 ? * 1 2007-2009"}) { 397 m_fieldCron.addItem(item); 398 } 399 m_fieldSiteRoot.setResourceFilter(CmsResourceFilter.DEFAULT_FOLDERS); 400 } 401 402 /** 403 * Initializes the form fields with values from the given job bean.<P> 404 * 405 * @param info the job bean with which to fill the form 406 */ 407 public void loadFromBean(CmsScheduledJobInfo info) { 408 409 // all other fields already populated by field group, we still need to handle the parameters 410 411 for (Map.Entry<String, String> entry : info.getParameters().entrySet()) { 412 addParamLine(entry.getKey(), entry.getValue()); 413 } 414 } 415 416 /** 417 * Try to save the form values to the edited bean.<p> 418 * 419 * @return true if setting the information was successful 420 */ 421 public boolean trySaveToBean() { 422 423 try { 424 m_group.commit(); 425 } catch (Exception e) { 426 LOG.info(e.getLocalizedMessage(), e); 427 return false; 428 } 429 m_job.setParameters(readParams()); 430 return true; 431 } 432 433 /** 434 * Adds a new parameter input field with the given content.<p> 435 * 436 * @param content the content 437 */ 438 void addParamLine(String content) { 439 440 m_paramContainer.addComponent(new ParamLine(content)); 441 } 442 443 /** 444 * Binds the given component to the given bean property.<p> 445 * 446 * @param field the component 447 * @param property the bean property 448 */ 449 void bindField(AbstractField<?> field, String property) { 450 451 m_group.bind(field, property); 452 453 field.setCaption(CmsVaadinUtils.getMessageText("label." + property)); 454 field.setDescription(CmsVaadinUtils.getMessageText("label." + property + ".help")); 455 } 456 457 /** 458 * Reads the job parameters from the parameter input fields.<p> 459 * 460 * @return the job parameters 461 */ 462 SortedMap<String, String> readParams() { 463 464 SortedMap<String, String> result = new TreeMap<String, String>(); 465 466 for (Component component : m_paramContainer) { 467 if (component instanceof ParamLine) { 468 ParamLine paramLine = (ParamLine)component; 469 String keyAndValue = paramLine.getValue(); 470 int eqPos = keyAndValue.indexOf("="); 471 if (eqPos >= 0) { 472 String key = keyAndValue.substring(0, eqPos); 473 String value = keyAndValue.substring(eqPos + 1); 474 result.put(key, value); 475 } 476 } 477 478 } 479 return result; 480 } 481 482 /** 483 * Adds a new widget for entering a job parameter.<p> 484 * 485 * @param key the preselected key 486 * @param value the preselected value 487 */ 488 private void addParamLine(String key, String value) { 489 490 addParamLine(key + "=" + value); 491 492 } 493}