001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.isis.objectstore.jdo.applib.service.command; 018 019import java.math.BigDecimal; 020import java.sql.Timestamp; 021import java.util.Map; 022import java.util.UUID; 023import java.util.concurrent.atomic.AtomicInteger; 024 025import javax.jdo.annotations.IdentityType; 026import javax.jdo.annotations.NotPersistent; 027 028import com.google.common.collect.Maps; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.isis.applib.DomainObjectContainer; 034import org.apache.isis.applib.annotation.ActionSemantics; 035import org.apache.isis.applib.annotation.ActionSemantics.Of; 036import org.apache.isis.applib.annotation.Bulk; 037import org.apache.isis.applib.annotation.Command.ExecuteIn; 038import org.apache.isis.applib.annotation.Command.Persistence; 039import org.apache.isis.applib.annotation.Hidden; 040import org.apache.isis.applib.annotation.Immutable; 041import org.apache.isis.applib.annotation.Mandatory; 042import org.apache.isis.applib.annotation.MemberGroupLayout; 043import org.apache.isis.applib.annotation.MemberOrder; 044import org.apache.isis.applib.annotation.MultiLine; 045import org.apache.isis.applib.annotation.Named; 046import org.apache.isis.applib.annotation.ObjectType; 047import org.apache.isis.applib.annotation.Programmatic; 048import org.apache.isis.applib.annotation.TypicalLength; 049import org.apache.isis.applib.annotation.Where; 050import org.apache.isis.applib.services.bookmark.Bookmark; 051import org.apache.isis.applib.services.bookmark.BookmarkService; 052import org.apache.isis.applib.services.command.Command; 053import org.apache.isis.applib.util.ObjectContracts; 054import org.apache.isis.applib.util.TitleBuffer; 055import org.apache.isis.objectstore.jdo.applib.service.DomainChangeJdoAbstract; 056import org.apache.isis.objectstore.jdo.applib.service.JdoColumnLength; 057import org.apache.isis.objectstore.jdo.applib.service.Util; 058import org.apache.isis.objectstore.jdo.applib.service.DomainChangeJdoAbstract.ChangeType; 059 060 061@javax.jdo.annotations.PersistenceCapable( 062 identityType=IdentityType.APPLICATION, 063 table="IsisCommand") 064@javax.jdo.annotations.Queries( { 065 @javax.jdo.annotations.Query( 066 name="findByTransactionId", language="JDOQL", 067 value="SELECT " 068 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 069 + "WHERE transactionId == :transactionId "), 070 @javax.jdo.annotations.Query( 071 name="findBackgroundCommandByTransactionId", language="JDOQL", 072 value="SELECT " 073 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 074 + "WHERE transactionId == :transactionId " 075 + "&& executeIn == 'BACKGROUND'"), 076 @javax.jdo.annotations.Query( 077 name="findBackgroundCommandsByParent", language="JDOQL", 078 value="SELECT " 079 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 080 + "WHERE parent == :parent " 081 + "&& executeIn == 'BACKGROUND'"), 082 @javax.jdo.annotations.Query( 083 name="findBackgroundCommandsNotYetStarted", language="JDOQL", 084 value="SELECT " 085 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 086 + "WHERE executeIn == 'BACKGROUND' " 087 + "&& startedAt == null " 088 + "ORDER BY timestamp ASC " 089 ), 090 @javax.jdo.annotations.Query( 091 name="findCurrent", language="JDOQL", 092 value="SELECT " 093 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 094 + "WHERE completedAt == null " 095 + "ORDER BY timestamp DESC"), 096 @javax.jdo.annotations.Query( 097 name="findCompleted", language="JDOQL", 098 value="SELECT " 099 + "FROM org.apache.isis.objectstore.jdo.applib.service.command.CommandJdo " 100 + "WHERE completedAt != null " 101 + "&& executeIn == 'FOREGROUND' " 102 + "ORDER BY timestamp DESC"), 103 @javax.jdo.annotations.Query( 104 name="findByTargetAndTimestampBetween", language="JDOQL", 105 value="SELECT " 106 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 107 + "WHERE targetStr == :targetStr " 108 + "&& timestamp >= :from " 109 + "&& timestamp <= :to " 110 + "ORDER BY timestamp DESC"), 111 @javax.jdo.annotations.Query( 112 name="findByTargetAndTimestampAfter", language="JDOQL", 113 value="SELECT " 114 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 115 + "WHERE targetStr == :targetStr " 116 + "&& timestamp >= :from " 117 + "ORDER BY timestamp DESC"), 118 @javax.jdo.annotations.Query( 119 name="findByTargetAndTimestampBefore", language="JDOQL", 120 value="SELECT " 121 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 122 + "WHERE targetStr == :targetStr " 123 + "&& timestamp <= :to " 124 + "ORDER BY timestamp DESC"), 125 @javax.jdo.annotations.Query( 126 name="findByTarget", language="JDOQL", 127 value="SELECT " 128 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 129 + "WHERE targetStr == :targetStr " 130 + "ORDER BY timestamp DESC"), 131 @javax.jdo.annotations.Query( 132 name="findByTimestampBetween", language="JDOQL", 133 value="SELECT " 134 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 135 + "WHERE timestamp >= :from " 136 + "&& timestamp <= :to " 137 + "ORDER BY timestamp DESC"), 138 @javax.jdo.annotations.Query( 139 name="findByTimestampAfter", language="JDOQL", 140 value="SELECT " 141 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 142 + "WHERE timestamp >= :from " 143 + "ORDER BY timestamp DESC"), 144 @javax.jdo.annotations.Query( 145 name="findByTimestampBefore", language="JDOQL", 146 value="SELECT " 147 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 148 + "WHERE timestamp <= :to " 149 + "ORDER BY timestamp DESC"), 150 @javax.jdo.annotations.Query( 151 name="find", language="JDOQL", 152 value="SELECT " 153 + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntryJdo " 154 + "ORDER BY timestamp DESC") 155}) 156@ObjectType("IsisCommand") 157@MemberGroupLayout( 158 columnSpans={6,0,6,12}, 159 left={"Identifiers","Target","Notes"}, 160 right={"Detail","Timings","Results"}) 161@Named("Command") 162@Immutable 163public class CommandJdo extends DomainChangeJdoAbstract implements Command { 164 165 @SuppressWarnings("unused") 166 private static final Logger LOG = LoggerFactory.getLogger(CommandJdo.class); 167 168 public CommandJdo() { 169 super(ChangeType.COMMAND); 170 } 171 172 173 174 // ////////////////////////////////////// 175 // Identification 176 // ////////////////////////////////////// 177 178 public String title() { 179 final TitleBuffer buf = new TitleBuffer(); 180 buf.append(getTargetStr()); 181 buf.append(" ").append(getMemberIdentifier()); 182 return buf.toString(); 183 } 184 185 186 187 // ////////////////////////////////////// 188 // user (property) 189 // ////////////////////////////////////// 190 191 private String user; 192 193 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.USER_NAME) 194 @MemberOrder(name="Identifiers", sequence = "10") 195 public String getUser() { 196 return user; 197 } 198 199 public void setUser(final String user) { 200 this.user = user; 201 } 202 203 204 205 // ////////////////////////////////////// 206 // timestamp (property) 207 // ////////////////////////////////////// 208 209 private Timestamp timestamp; 210 211 /** 212 * The date/time at which this action was created. 213 */ 214 @javax.jdo.annotations.Persistent 215 @javax.jdo.annotations.Column(allowsNull="false") 216 @MemberOrder(name="Identifiers", sequence = "20") 217 public Timestamp getTimestamp() { 218 return timestamp; 219 } 220 221 /** 222 * <b>NOT API</b>: intended to be called only by the framework. 223 */ 224 public void setTimestamp(final Timestamp timestamp) { 225 this.timestamp = timestamp; 226 } 227 228 229 // ////////////////////////////////////// 230 // executor (property) 231 // ////////////////////////////////////// 232 233 private Executor executor; 234 235 @Programmatic 236 @javax.jdo.annotations.NotPersistent 237 @Override 238 public Executor getExecutor() { 239 return executor; 240 } 241 242 @Override 243 public void setExecutor(Executor nature) { 244 this.executor = nature; 245 } 246 247 // ////////////////////////////////////// 248 // executeIn (property) 249 // ////////////////////////////////////// 250 251 private ExecuteIn executeIn; 252 253 /** 254 * Whether the action was invoked explicitly by the user, or scheduled as a background 255 * task, or as for some other reason, eg a side-effect of rendering an object due to 256 * get-after-post). 257 */ 258 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.Command.EXECUTE_IN) 259 @MemberOrder(name="Identifiers", sequence = "32") 260 @Override 261 public ExecuteIn getExecuteIn() { 262 return executeIn; 263 } 264 265 /** 266 * <b>NOT API</b>: intended to be called only by the framework. 267 */ 268 @Override 269 public void setExecuteIn(ExecuteIn nature) { 270 this.executeIn = nature; 271 } 272 273 274 // ////////////////////////////////////// 275 // parent (property) 276 // ////////////////////////////////////// 277 278 private Command parent; 279 280 @Override 281 @javax.jdo.annotations.Persistent 282 @javax.jdo.annotations.Column(name="parentTransactionId", allowsNull="true") 283 @Hidden(where=Where.PARENTED_TABLES) 284 @MemberOrder(name="Identifiers",sequence = "40") 285 public Command getParent() { 286 return parent; 287 } 288 289 @Override 290 public void setParent(Command parent) { 291 this.parent = parent; 292 } 293 294 295 // ////////////////////////////////////// 296 // transactionId (property) 297 // ////////////////////////////////////// 298 299 300 private UUID transactionId; 301 302 /** 303 * The unique identifier (a GUID) of the transaction in which this command occurred. 304 */ 305 @javax.jdo.annotations.PrimaryKey 306 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.TRANSACTION_ID) 307 @TypicalLength(JdoColumnLength.TRANSACTION_ID) 308 @MemberOrder(name="Identifiers",sequence = "50") 309 @Override 310 public UUID getTransactionId() { 311 return transactionId; 312 } 313 314 /** 315 * <b>NOT API</b>: intended to be called only by the framework. 316 * 317 * <p> 318 * Implementation notes: copied over from the Isis transaction when the command is persisted. 319 */ 320 @Override 321 public void setTransactionId(final UUID transactionId) { 322 this.transactionId = transactionId; 323 } 324 325 326 // ////////////////////////////////////// 327 // targetClass (property) 328 // ////////////////////////////////////// 329 330 private String targetClass; 331 332 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.TARGET_CLASS) 333 @TypicalLength(30) 334 @MemberOrder(name="Target", sequence = "10") 335 @Named("Class") 336 public String getTargetClass() { 337 return targetClass; 338 } 339 340 public void setTargetClass(final String targetClass) { 341 this.targetClass = Util.abbreviated(targetClass, JdoColumnLength.TARGET_CLASS); 342 } 343 344 345 // ////////////////////////////////////// 346 // targetAction (property) 347 // ////////////////////////////////////// 348 349 private String targetAction; 350 351 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.TARGET_ACTION) 352 @Mandatory 353 @Hidden(where=Where.NOWHERE) 354 @TypicalLength(30) 355 @MemberOrder(name="Target", sequence = "20") 356 @Named("Action") 357 public String getTargetAction() { 358 return targetAction; 359 } 360 361 public void setTargetAction(final String targetAction) { 362 this.targetAction = Util.abbreviated(targetAction, JdoColumnLength.TARGET_ACTION); 363 } 364 365 366 // ////////////////////////////////////// 367 // target (property) 368 // openTargetObject (action) 369 // ////////////////////////////////////// 370 371 private String targetStr; 372 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.BOOKMARK, name="target") 373 @Hidden(where=Where.ALL_TABLES) 374 @MemberOrder(name="Target", sequence="30") 375 @Named("Object") 376 public String getTargetStr() { 377 return targetStr; 378 } 379 380 public void setTargetStr(final String targetStr) { 381 this.targetStr = targetStr; 382 } 383 384 // ////////////////////////////////////// 385 // arguments (property) 386 // ////////////////////////////////////// 387 388 private String arguments; 389 390 @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB") 391 @MultiLine(numberOfLines=7) 392 @Hidden(where=Where.ALL_TABLES) 393 @MemberOrder(name="Target",sequence = "40") 394 public String getArguments() { 395 return arguments; 396 } 397 398 public void setArguments(final String arguments) { 399 this.arguments = arguments; 400 } 401 402 403 404 // ////////////////////////////////////// 405 // memberIdentifier (property) 406 // ////////////////////////////////////// 407 408 private String memberIdentifier; 409 410 @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.MEMBER_IDENTIFIER) 411 @TypicalLength(60) 412 @Hidden(where=Where.ALL_TABLES) 413 @MemberOrder(name="Detail",sequence = "1") 414 public String getMemberIdentifier() { 415 return memberIdentifier; 416 } 417 418 public void setMemberIdentifier(final String memberIdentifier) { 419 this.memberIdentifier = Util.abbreviated(memberIdentifier, JdoColumnLength.MEMBER_IDENTIFIER); 420 } 421 422 423 424 // ////////////////////////////////////// 425 // memento (property) 426 // ////////////////////////////////////// 427 428 private String memento; 429 430 @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB") 431 @MultiLine(numberOfLines=9) 432 @Hidden(where=Where.ALL_TABLES) 433 @MemberOrder(name="Detail",sequence = "30") 434 public String getMemento() { 435 return memento; 436 } 437 438 public void setMemento(final String memento) { 439 this.memento = memento; 440 } 441 442 443 444 // ////////////////////////////////////// 445 // startedAt (derived property) 446 // ////////////////////////////////////// 447 448 private Timestamp startedAt; 449 450 @javax.jdo.annotations.Persistent 451 @javax.jdo.annotations.Column(allowsNull="true") 452 @MemberOrder(name="Timings", sequence = "3") 453 public Timestamp getStartedAt() { 454 return startedAt; 455 } 456 457 /** 458 * <b>NOT API</b>: intended to be called only by the framework. 459 */ 460 public void setStartedAt(final Timestamp startedAt) { 461 this.startedAt = startedAt; 462 } 463 464 465 466 // ////////////////////////////////////// 467 // completedAt (property) 468 // ////////////////////////////////////// 469 470 private Timestamp completedAt; 471 472 /** 473 * The date/time at which this interaction completed. 474 */ 475 @javax.jdo.annotations.Persistent 476 @javax.jdo.annotations.Column(allowsNull="true") 477 @MemberOrder(name="Timings", sequence = "4") 478 @Override 479 public Timestamp getCompletedAt() { 480 return completedAt; 481 } 482 483 @Override 484 public void setCompletedAt(final Timestamp completed) { 485 this.completedAt = completed; 486 } 487 488 489 // ////////////////////////////////////// 490 // duration (derived property) 491 // ////////////////////////////////////// 492 493 /** 494 * The number of seconds (to 3 decimal places) that this interaction lasted. 495 * 496 * <p> 497 * Populated only if it has {@link #getCompletedAt() completed}. 498 */ 499 @javax.validation.constraints.Digits(integer=5, fraction=3) 500 @Named("Duration") 501 @MemberOrder(name="Timings", sequence = "7") 502 public BigDecimal getDuration() { 503 return Util.durationBetween(getStartedAt(), getCompletedAt()); 504 } 505 506 507 508 // ////////////////////////////////////// 509 // complete (derived property) 510 // ////////////////////////////////////// 511 512 513 @javax.jdo.annotations.NotPersistent 514 @MemberOrder(name="Timings", sequence = "8") 515 @Hidden(where=Where.OBJECT_FORMS) 516 public boolean isComplete() { 517 return getCompletedAt() != null; 518 } 519 520 521 522 // ////////////////////////////////////// 523 // state (derived property) 524 // ////////////////////////////////////// 525 526 @javax.jdo.annotations.NotPersistent 527 @MemberOrder(name="Results",sequence = "10") 528 @Hidden(where=Where.OBJECT_FORMS) 529 @Named("Result") 530 public String getResultSummary() { 531 if(getCompletedAt() == null) { 532 return ""; 533 } 534 if(getException() != null) { 535 return "EXCEPTION"; 536 } 537 if(getResultStr() != null) { 538 return "OK"; 539 } else { 540 return "OK (VOID)"; 541 } 542 } 543 544 545 // ////////////////////////////////////// 546 // result (property) 547 // openResultObject (action) 548 // ////////////////////////////////////// 549 550 @Programmatic 551 @Override 552 public Bookmark getResult() { 553 return Util.bookmarkFor(getResultStr()); 554 } 555 556 @Programmatic 557 @Override 558 public void setResult(Bookmark result) { 559 setResultStr(Util.asString(result)); 560 } 561 562 // ////////////////////////////////////// 563 564 private String resultStr; 565 566 @javax.jdo.annotations.Column(allowsNull="true", length=JdoColumnLength.BOOKMARK, name="result") 567 @Hidden(where=Where.ALL_TABLES) 568 @Named("Result Bookmark") 569 @MemberOrder(name="Results", sequence="25") 570 public String getResultStr() { 571 return resultStr; 572 } 573 574 public void setResultStr(final String resultStr) { 575 this.resultStr = resultStr; 576 } 577 578 // ////////////////////////////////////// 579 580 @ActionSemantics(Of.SAFE) 581 @MemberOrder(name="ResultStr", sequence="1") 582 @Named("Open") 583 public Object openResultObject() { 584 return Util.lookupBookmark(getResult(), bookmarkService, container); 585 } 586 public boolean hideOpenResultObject() { 587 return getResult() == null; 588 } 589 590 591 // ////////////////////////////////////// 592 // exception (property) 593 // causedException (derived property) 594 // showException (associated action) 595 // ////////////////////////////////////// 596 597 private String exception; 598 599 /** 600 * Stack trace of any exception that might have occurred if this interaction/transaction aborted. 601 * 602 * <p> 603 * Not visible in the UI, but accessible 604 * <p> 605 * Not part of the applib API, because the default implementation is not persistent 606 * and so there's no object that can be accessed to be annotated. 607 */ 608 @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB") 609 @Hidden 610 @Override 611 public String getException() { 612 return exception; 613 } 614 615 @Override 616 public void setException(final String exception) { 617 this.exception = exception; 618 } 619 620 621 // ////////////////////////////////////// 622 623 @javax.jdo.annotations.NotPersistent 624 @MemberOrder(name="Results",sequence = "30") 625 @Hidden(where=Where.ALL_TABLES) 626 public boolean isCausedException() { 627 return getException() != null; 628 } 629 630 631 // ////////////////////////////////////// 632 633 @ActionSemantics(Of.SAFE) 634 @MemberOrder(name="causedException", sequence = "1") 635 public String showException() { 636 return getException(); 637 } 638 public boolean hideShowException() { 639 return !isCausedException(); 640 } 641 642 643 // ////////////////////////////////////// 644 // next(...) impl 645 // ////////////////////////////////////// 646 647 private final Map<String, AtomicInteger> sequenceByName = Maps.newHashMap(); 648 649 650 651 @Programmatic 652 @Override 653 public int next(String sequenceName) { 654 AtomicInteger next = sequenceByName.get(sequenceName); 655 if(next == null) { 656 next = new AtomicInteger(0); 657 sequenceByName.put(sequenceName, next); 658 } else { 659 next.incrementAndGet(); 660 } 661 return next.get(); 662 } 663 664 665 // ////////////////////////////////////// 666 // persistence (programmatic) 667 // ////////////////////////////////////// 668 669 private Persistence persistence; 670 671 @javax.jdo.annotations.NotPersistent 672 @Programmatic 673 @Override 674 public Persistence getPersistence() { 675 return persistence; 676 } 677 678 @Override 679 public void setPersistence(Persistence persistence) { 680 this.persistence = persistence; 681 } 682 683 684 // ////////////////////////////////////// 685 // setPersistHint (SPI impl) 686 // ////////////////////////////////////// 687 688 private boolean persistHint; 689 690 @NotPersistent 691 @Programmatic 692 public boolean isPersistHint() { 693 return persistHint; 694 } 695 696 @Programmatic 697 @Override 698 public void setPersistHint(boolean persistHint) { 699 this.persistHint = persistHint; 700 } 701 702 703 // ////////////////////////////////////// 704 705 @Programmatic 706 boolean shouldPersist() { 707 if(Persistence.PERSISTED == getPersistence()) { 708 return true; 709 } 710 if(Persistence.IF_HINTED == getPersistence()) { 711 return isPersistHint(); 712 } 713 return false; 714 } 715 716 717 718 // ////////////////////////////////////// 719 // toString 720 // ////////////////////////////////////// 721 722 @Override 723 public String toString() { 724 return ObjectContracts.toString(this, "targetStr,memberIdentifier,user,startedAt,completedAt,duration,transactionId"); 725 } 726 727 728 729 // ////////////////////////////////////// 730 // dependencies 731 // ////////////////////////////////////// 732 733 734 @javax.inject.Inject 735 private BookmarkService bookmarkService; 736 737 @javax.inject.Inject 738 private DomainObjectContainer container; 739 740}