001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Julien Anguenot 018 * Thierry Delprat 019 * Florent Guillaume 020 */ 021 022package org.nuxeo.ecm.platform.audit.impl; 023 024import java.io.IOException; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.Map; 028 029import javax.persistence.CascadeType; 030import javax.persistence.Column; 031import javax.persistence.Entity; 032import javax.persistence.GeneratedValue; 033import javax.persistence.GenerationType; 034import javax.persistence.Id; 035import javax.persistence.JoinColumn; 036import javax.persistence.JoinTable; 037import javax.persistence.MapKey; 038import javax.persistence.NamedQueries; 039import javax.persistence.NamedQuery; 040import javax.persistence.OneToMany; 041import javax.persistence.Table; 042import javax.persistence.Temporal; 043import javax.persistence.TemporalType; 044import javax.persistence.Transient; 045 046import com.fasterxml.jackson.annotation.JsonProperty; 047import com.fasterxml.jackson.core.JsonGenerator; 048import com.fasterxml.jackson.databind.JsonSerializer; 049import com.fasterxml.jackson.databind.SerializerProvider; 050import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 051import com.fasterxml.jackson.databind.annotation.JsonSerialize; 052import org.apache.commons.lang.builder.ToStringBuilder; 053import org.nuxeo.ecm.core.api.DocumentModel; 054import org.nuxeo.ecm.core.api.DocumentRef; 055import org.nuxeo.ecm.platform.audit.api.ExtendedInfo; 056import org.nuxeo.ecm.platform.audit.api.LogEntry; 057import org.nuxeo.ecm.platform.audit.api.comment.UIAuditComment; 058import org.nuxeo.ecm.platform.audit.io.ExtendedInfoDeserializer; 059import org.nuxeo.ecm.platform.audit.io.ExtendedInfoSerializer; 060 061/** 062 * Log entry implementation. 063 */ 064@Entity(name = "LogEntry") 065@NamedQueries({ 066 @NamedQuery(name = "LogEntry.removeByEventIdAndPath", query = "delete LogEntry log where log.eventId = :eventId and log.docPath like :pathPattern"), 067 @NamedQuery(name = "LogEntry.findByDocument", query = "from LogEntry log where log.docUUID=:docUUID ORDER BY log.eventDate DESC"), 068 @NamedQuery(name = "LogEntry.findByDocumentAndRepository", query = "from LogEntry log where log.docUUID=:docUUID and log.repositoryId=:repositoryId ORDER BY log.eventDate DESC"), 069 @NamedQuery(name = "LogEntry.findAll", query = "from LogEntry log order by log.eventDate DESC"), 070 @NamedQuery(name = "LogEntry.findByEventIdAndPath", query = "from LogEntry log where log.eventId=:eventId and log.docPath LIKE :pathPattern"), 071 @NamedQuery(name = "LogEntry.findByHavingExtendedInfo", query = "from LogEntry log where log.extendedInfos['one'] is not null order by log.eventDate DESC"), 072 @NamedQuery(name = "LogEntry.countEventsById", query = "select count(log.eventId) from LogEntry log where log.eventId=:eventId"), 073 @NamedQuery(name = "LogEntry.findEventIds", query = "select distinct log.eventId from LogEntry log") }) 074@Table(name = "NXP_LOGS") 075public class LogEntryImpl implements LogEntry { 076 077 @JsonProperty("entity-type") 078 protected String entityType; 079 080 private static final long serialVersionUID = 3037187381843636097L; 081 082 private long id; 083 084 private String principalName; 085 086 private String eventId; 087 088 private Date eventDate; 089 090 private Date logDate; 091 092 private String docUUID; 093 094 private String docType; 095 096 private String docPath; 097 098 private String category; 099 100 private String comment; 101 102 private String docLifeCycle; 103 104 private String repositoryId; 105 106 protected transient UIAuditComment uiComment; 107 108 private Map<String, ExtendedInfoImpl> extendedInfos = new HashMap<String, ExtendedInfoImpl>(); 109 110 /** 111 * @return the log identifier 112 */ 113 @Override 114 @Id 115 @GeneratedValue(strategy = GenerationType.AUTO) 116 @Column(name = "LOG_ID", nullable = false, columnDefinition = "integer") 117 public long getId() { 118 return id; 119 } 120 121 @Override 122 public void setId(long id) { 123 this.id = id; 124 } 125 126 /** 127 * Returns the name of the principal who originated the log entry. 128 * 129 * @return the name of the principal who originated the log entry 130 */ 131 @Override 132 @Column(name = "LOG_PRINCIPAL_NAME") 133 public String getPrincipalName() { 134 return principalName; 135 } 136 137 @Override 138 public void setPrincipalName(String principalName) { 139 this.principalName = principalName; 140 } 141 142 /** 143 * Returns the identifier of the event that originated the log entry. 144 * 145 * @return the identifier of the event that originated the log entry 146 */ 147 @Override 148 @Column(name = "LOG_EVENT_ID", nullable = false) 149 @MapKey(name = "logKey") 150 public String getEventId() { 151 return eventId; 152 } 153 154 @Override 155 public void setEventId(String eventId) { 156 this.eventId = eventId; 157 } 158 159 /** 160 * Returns the date of the event that originated the log entry. 161 * 162 * @return the date of the event that originated the log entry 163 */ 164 @Override 165 @Temporal(TemporalType.TIMESTAMP) 166 @JsonSerialize(using = DateSerializer.class) 167 @Column(name = "LOG_EVENT_DATE") 168 public Date getEventDate() { 169 return eventDate; 170 } 171 172 @Override 173 public void setEventDate(Date eventDate) { 174 this.eventDate = eventDate; 175 } 176 177 /** 178 * @return the date of the log insertion: this up to max transaction timeout later than eventDate. This date is 179 * useful for services such as Nuxeo Drive that need fine grained incremental near-monotonic access to the 180 * audit log. 181 * @since 5.7 182 * @since 5.6-HF16 183 */ 184 @Override 185 @Temporal(TemporalType.TIMESTAMP) 186 @JsonSerialize(using = DateSerializer.class) 187 @Column(name = "LOG_DATE") 188 public Date getLogDate() { 189 return logDate; 190 } 191 192 @Override 193 public void setLogDate(Date logDate) { 194 this.logDate = logDate; 195 } 196 197 /** 198 * Returns the doc UUID related to the log entry. 199 * <p> 200 * It might be null if the event that originated the event is noe bound to any document. 201 * 202 * @return the doc UUID related to the log entry. 203 */ 204 @Override 205 @Column(name = "LOG_DOC_UUID") 206 public String getDocUUID() { 207 return docUUID; 208 } 209 210 @Override 211 public void setDocUUID(String docUUID) { 212 this.docUUID = docUUID; 213 } 214 215 @Override 216 public void setDocUUID(DocumentRef docRef) { 217 switch (docRef.type()) { 218 case DocumentRef.ID: 219 docUUID = (String) docRef.reference(); 220 break; 221 case DocumentRef.INSTANCE: 222 docUUID = ((DocumentModel) docRef.reference()).getId(); 223 break; 224 default: 225 throw new IllegalArgumentException("not an id reference " + docRef); 226 } 227 } 228 229 /** 230 * Returns the doc path related to the log entry. 231 * <p> 232 * It might be null if the event that originated the event is noe bound to any document. 233 * 234 * @return the doc path related to the log entry. 235 */ 236 @Override 237 @Column(name = "LOG_DOC_PATH", length = 1024) 238 public String getDocPath() { 239 return docPath; 240 } 241 242 @Override 243 public void setDocPath(String docPath) { 244 this.docPath = docPath; 245 } 246 247 /** 248 * Returns the doc type related to the log entry. 249 * <p> 250 * It might be null if the event that originated the event is not bound to any document. 251 * 252 * @return the doc type related to the log entry. 253 */ 254 @Override 255 @Column(name = "LOG_DOC_TYPE") 256 public String getDocType() { 257 return docType; 258 } 259 260 @Override 261 public void setDocType(String docType) { 262 this.docType = docType; 263 } 264 265 /** 266 * Returns the category for this log entry. 267 * <p> 268 * This is defined at client level. Categories are not restricted in any ways. 269 * 270 * @return the category for this log entry. 271 */ 272 @Override 273 @Column(name = "LOG_EVENT_CATEGORY") 274 public String getCategory() { 275 return category; 276 } 277 278 @Override 279 public void setCategory(String category) { 280 this.category = category; 281 } 282 283 /** 284 * Returns the associated comment for this log entry. 285 * 286 * @return the associated comment for this log entry 287 */ 288 @Override 289 @Column(name = "LOG_EVENT_COMMENT", length = 1024) 290 public String getComment() { 291 return comment; 292 } 293 294 @Override 295 public void setComment(String comment) { 296 this.comment = comment; 297 } 298 299 /** 300 * Return the life cycle if the document related to the log entry. 301 * <p> 302 * It might be null if the event that originated the event is noe bound to any document. 303 * 304 * @return the life cycle if the document related to the log entry. 305 */ 306 @Override 307 @Column(name = "LOG_DOC_LIFE_CYCLE") 308 public String getDocLifeCycle() { 309 return docLifeCycle; 310 } 311 312 @Override 313 public void setDocLifeCycle(String docLifeCycle) { 314 this.docLifeCycle = docLifeCycle; 315 } 316 317 /** 318 * Returns the repository id related to the log entry. 319 * 320 * @return the repository id 321 */ 322 @Override 323 @Column(name = "LOG_REPO_ID") 324 public String getRepositoryId() { 325 return repositoryId; 326 } 327 328 @Override 329 public void setRepositoryId(String repositoryId) { 330 this.repositoryId = repositoryId; 331 } 332 333 // public Map<String, ExtendedInfoImpl> getExtendedInfosImpl() { 334 // return extendedInfos; 335 // } 336 // 337 // public void setExtendedInfosImpl(Map<String, ExtendedInfoImpl> infos) { 338 // extendedInfos = infos; 339 // } 340 341 @Override 342 @JsonProperty("extended") 343 @JsonSerialize(keyAs = String.class, contentUsing = ExtendedInfoSerializer.class) 344 @OneToMany(cascade = CascadeType.ALL, targetEntity = ExtendedInfoImpl.class) 345 @JoinTable(name = "NXP_LOGS_MAPEXTINFOS", joinColumns = { @JoinColumn(name = "LOG_FK") }, inverseJoinColumns = { 346 @JoinColumn(name = "INFO_FK") }) 347 @org.hibernate.annotations.MapKey(columns = { @Column(name = "mapkey", nullable = false) }) 348 public Map<String, ExtendedInfo> getExtendedInfos() { 349 return (Map) extendedInfos; 350 // return (Map)getExtendedInfosImpl(); 351 } 352 353 @Override 354 @JsonDeserialize(keyAs = String.class, contentUsing = ExtendedInfoDeserializer.class) 355 public void setExtendedInfos(Map<String, ExtendedInfo> infos) { 356 extendedInfos = (Map) infos; 357 // setExtendedInfosImpl((Map)infos); 358 } 359 360 @Override 361 public String toString() { 362 return ToStringBuilder.reflectionToString(this); 363 } 364 365 @Transient 366 @Override 367 public UIAuditComment getPreprocessedComment() { 368 return uiComment; 369 } 370 371 @Override 372 public void setPreprocessedComment(UIAuditComment uiComment) { 373 this.uiComment = uiComment; 374 } 375 376 /** 377 * Specific date serializer to have compliant dates with both current and 8.10 Elasticsearch mapping 378 * 379 * @since 9.3 380 */ 381 static class DateSerializer extends JsonSerializer<Date> { 382 383 public DateSerializer() { 384 } 385 386 @Override 387 public void serialize(Date date, JsonGenerator jg, SerializerProvider serializers) throws IOException { 388 jg.writeObject(date.toInstant().toString()); 389 } 390 } 391 392}