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