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}