/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.core.runtime.services.publish; import java.sql.Timestamp; import java.util.Collection; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import com.google.common.base.Function; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimaps; import org.apache.isis.applib.annotation.Programmatic; import org.apache.isis.applib.annotation.PublishedObject; import org.apache.isis.applib.services.publish.PublishedObjects; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.oid.RootOid; import org.apache.isis.schema.chg.v1.ChangesDto; import org.apache.isis.schema.chg.v1.ObjectsDto; import org.apache.isis.schema.common.v1.OidDto; import org.apache.isis.schema.common.v1.OidsDto; import org.apache.isis.schema.utils.jaxbadapters.JavaSqlTimestampXmlGregorianCalendarAdapter; /** * Captures which objects were created, updated or deleted in the course of a transaction. */ public class PublishedObjectsDefault implements PublishedObjects { //region > constructor, fields private UUID transactionUuid; private final int sequence; private final String userName; private final Timestamp completedAt; private final int numberLoaded; private final int numberObjectPropertiesModified; private final Map<ObjectAdapter, PublishedObject.ChangeKind> changesByAdapter; public PublishedObjectsDefault( final UUID transactionUuid, final int sequence, final String userName, final Timestamp completedAt, final int numberLoaded, final int numberObjectPropertiesModified, final Map<ObjectAdapter, PublishedObject.ChangeKind> changesByAdapter) { this.transactionUuid = transactionUuid; this.sequence = sequence; this.userName = userName; this.completedAt = completedAt; this.numberLoaded = numberLoaded; this.numberObjectPropertiesModified = numberObjectPropertiesModified; this.changesByAdapter = changesByAdapter; } //endregion //region > transactionId, completedAt, user @Programmatic @Override public UUID getTransactionId() { return transactionUuid; } /** * Unused; the {@link #getTransactionId()} is set in the constructor. */ @Override public void setTransactionId(final UUID transactionId) { this.transactionUuid = transactionId; } /** * The date/time at which this set of enlisted objects was created (approx the completion time of the transaction). */ @Override public Timestamp getCompletedAt() { return completedAt; } @Override public String getUsername() { return userName; } //endregion //region > dto /** * lazily computed */ private ChangesDto dto; @Override public ChangesDto getDto() { return dto != null ? dto : (dto = newDto()); } //endregion //region > numberLoaded, numberCreated, numberUpdated, numberDeleted, numberObjectPropertiesModified @Override public int getNumberLoaded() { return numberLoaded; } @Override public int getNumberCreated() { return numAdaptersOfKind(PublishedObject.ChangeKind.CREATE); } @Override public int getNumberUpdated() { return numAdaptersOfKind(PublishedObject.ChangeKind.UPDATE); } @Override public int getNumberDeleted() { return numAdaptersOfKind(PublishedObject.ChangeKind.DELETE); } @Override public int getNumberPropertiesModified() { return numberObjectPropertiesModified; } private int numAdaptersOfKind(final PublishedObject.ChangeKind kind) { final Collection<ObjectAdapter> objectAdapters = adaptersByChange().get(kind); return objectAdapters != null ? objectAdapters.size() : 0; } /** * Lazily populated */ private Map<PublishedObject.ChangeKind, Collection<ObjectAdapter>> adaptersByChange; private Map<PublishedObject.ChangeKind, Collection<ObjectAdapter>> adaptersByChange() { return adaptersByChange != null? adaptersByChange : (adaptersByChange = invert(changesByAdapter)); } private static <T, S> Map<T, Collection<S>> invert(final Map<S, T> valueByKey) { return new TreeMap<>( Multimaps.invertFrom( Multimaps.forMap(valueByKey), ArrayListMultimap.<T, S>create() ).asMap() ); } //endregion //region > newDto, newObjectsDto, newChangesDto private ChangesDto newDto() { final ObjectsDto objectsDto = newObjectsDto(); return newChangesDto(objectsDto); } protected ObjectsDto newObjectsDto() { final ObjectsDto objectsDto = new ObjectsDto(); objectsDto.setCreated(oidsDtoFor(PublishedObject.ChangeKind.CREATE)); objectsDto.setUpdated(oidsDtoFor(PublishedObject.ChangeKind.UPDATE)); objectsDto.setDeleted(oidsDtoFor(PublishedObject.ChangeKind.DELETE)); objectsDto.setLoaded(getNumberLoaded()); objectsDto.setPropertiesModified(getNumberPropertiesModified()); return objectsDto; } private OidsDto oidsDtoFor(final PublishedObject.ChangeKind kind) { final OidsDto oidsDto = new OidsDto(); final Map<PublishedObject.ChangeKind, Collection<ObjectAdapter>> adaptersByChange = adaptersByChange(); final Collection<ObjectAdapter> adapters = adaptersByChange.get(kind); if(adapters != null) { final ImmutableList<OidDto> oidDtos = FluentIterable.from(adapters) .transform(new Function<ObjectAdapter, OidDto>() { @Override public OidDto apply(final ObjectAdapter objectAdapter) { final RootOid rootOid = (RootOid) objectAdapter.getOid(); return rootOid.asOidDto(); } }) .toList(); oidsDto.getOid().addAll(oidDtos); } return oidsDto; } protected ChangesDto newChangesDto(final ObjectsDto objectsDto) { final ChangesDto changesDto = new ChangesDto(); changesDto.setMajorVersion("1"); changesDto.setMinorVersion("0"); changesDto.setTransactionId(transactionUuid.toString()); changesDto.setSequence(sequence); changesDto.setUser(userName); changesDto.setCompletedAt(JavaSqlTimestampXmlGregorianCalendarAdapter.print(completedAt)); changesDto.setObjects(objectsDto); return changesDto; } //endregion }