diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorRowAttributesProvider.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorRowAttributesProvider.java index a8bfd8dd1..8233d0c86 100644 --- a/src/main/java/de/metas/ui/web/handlingunits/HUEditorRowAttributesProvider.java +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorRowAttributesProvider.java @@ -47,6 +47,11 @@ class HUEditorRowAttributesProvider implements IDocumentViewAttributesProvider { + public static final HUEditorRowAttributesProvider newInstance() + { + return new HUEditorRowAttributesProvider(); + } + private final ExtendedMemorizingSupplier _attributeStorageFactory = ExtendedMemorizingSupplier.of(() -> createAttributeStorageFactory()); private final ConcurrentHashMap documentViewAttributesByKey = new ConcurrentHashMap<>(); @@ -57,7 +62,7 @@ private static final class DocumentViewAttributesKey private DocumentId huId; } - public HUEditorRowAttributesProvider() + private HUEditorRowAttributesProvider() { super(); } diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorView.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorView.java index 28e8ed5de..a55802c42 100644 --- a/src/main/java/de/metas/ui/web/handlingunits/HUEditorView.java +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorView.java @@ -1,11 +1,8 @@ package de.metas.ui.web.handlingunits; import java.util.Collection; -import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -16,14 +13,12 @@ import org.adempiere.util.Check; import org.adempiere.util.GuavaCollectors; import org.adempiere.util.Services; -import org.adempiere.util.lang.ExtendedMemorizingSupplier; import org.adempiere.util.lang.impl.TableRecordReference; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Evaluatee; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import de.metas.handlingunits.model.I_M_HU; @@ -85,22 +80,25 @@ public static HUEditorView cast(final IDocumentViewSelection view) private final Set referencingDocumentPaths; - private final CopyOnWriteArraySet huIds; - private final HUEditorViewRepository huEditorRepo; - private final ExtendedMemorizingSupplier _recordsSupplier = ExtendedMemorizingSupplier.of(() -> retrieveHUEditorRows()); - private final ViewActionDescriptorsList actions; + private final HUEditorViewBuffer rowsBuffer; + private final HUEditorRowAttributesProvider attributesProvider; private HUEditorView(final Builder builder) { super(); parentViewId = builder.getParentViewId(); - viewId = builder.getViewId(); + + this.attributesProvider = HUEditorRowAttributesProvider.newInstance(); + final HUEditorViewRepository huEditorRepo = HUEditorViewRepository.builder() + .windowId(viewId.getWindowId()) + .referencingTableName(builder.getReferencingTableName()) + .attributesProvider(attributesProvider) + .build(); - huIds = new CopyOnWriteArraySet<>(builder.getHUIds()); - huEditorRepo = builder.createHUEditorRepository(); + rowsBuffer = new HUEditorViewBuffer_FullyCached(huEditorRepo, builder.getHUIds()); referencingDocumentPaths = builder.getReferencingDocumentPaths(); @@ -128,7 +126,7 @@ public String getTableName() @Override public long size() { - return getRows().size(); + return rowsBuffer.size(); } @Override @@ -152,17 +150,9 @@ public boolean isQueryLimitHit() @Override public DocumentViewResult getPage(final int firstRow, final int pageLength, final List orderBys) { - Stream stream = getRows().stream() - .skip(firstRow) - .limit(pageLength); - - final Comparator comparator = createComparatorOrNull(orderBys); - if (comparator != null) - { - stream = stream.sorted(comparator); - } - - final List page = stream.collect(GuavaCollectors.toImmutableList()); + final List page = rowsBuffer + .streamPage(firstRow, pageLength, orderBys) + .collect(GuavaCollectors.toImmutableList()); return DocumentViewResult.ofViewAndPage(this, firstRow, pageLength, orderBys, page); } @@ -173,34 +163,10 @@ public ViewActionDescriptorsList getActions() return actions; } - private static final Comparator createComparatorOrNull(final List orderBys) - { - if (orderBys == null || orderBys.isEmpty()) - { - return null; - } - - Comparator comparator = null; - for (final DocumentQueryOrderBy orderBy : orderBys) - { - final Comparator orderByComparator = orderBy. asComparator((row, fieldName) -> row.getFieldValueAsJson(fieldName)); - if (comparator == null) - { - comparator = orderByComparator; - } - else - { - comparator = comparator.thenComparing(orderByComparator); - } - } - - return comparator; - } - @Override public HUEditorRow getById(final DocumentId rowId) throws EntityNotFoundException { - return getRows().getById(rowId); + return rowsBuffer.getById(rowId); } @Override @@ -270,41 +236,37 @@ public void invalidateAll() private void invalidateAllNoNotify() { - _recordsSupplier.forget(); - huEditorRepo.invalidateAll(); + attributesProvider.invalidateAll(); + rowsBuffer.invalidateAll(); } public void addHUsAndInvalidate(final Collection husToAdd) { - if (husToAdd.isEmpty()) + if (rowsBuffer.addHUIds(extractHUIds(husToAdd))) { - return; + invalidateAll(); } - - huIds.addAll(extractHUIds(husToAdd)); - invalidateAll(); } public void addHUAndInvalidate(final I_M_HU hu) { - if (hu == null) + if (hu == null || hu.getM_HU_ID() <= 0) { return; } - huIds.add(hu.getM_HU_ID()); - invalidateAll(); + if (rowsBuffer.addHUIds(ImmutableSet.of(hu.getM_HU_ID()))) + { + invalidateAll(); + } } public void removesHUsAndInvalidate(final Collection husToRemove) { - if (husToRemove == null || husToRemove.isEmpty()) + if (rowsBuffer.removeHUIds(extractHUIds(husToRemove))) { - return; + invalidateAll(); } - - huIds.removeAll(extractHUIds(husToRemove)); - invalidateAll(); } private static final Set extractHUIds(final Collection hus) @@ -314,27 +276,23 @@ private static final Set extractHUIds(final Collection hus) return ImmutableSet.of(); } - return hus.stream().map(I_M_HU::getM_HU_ID).collect(Collectors.toSet()); + return hus.stream().filter(hu -> hu != null).map(I_M_HU::getM_HU_ID).collect(Collectors.toSet()); } @Override public void notifyRecordsChanged(final Set recordRefs) { - final IndexedHUEditorRows records = getRecordsNoLoad(); - if (records == null) - { - return; - } - // TODO: notifyRecordsChanged: // get M_HU_IDs from recordRefs, // find the top level records from this view which contain our HUs // invalidate those top levels only - final boolean containsSomeRecords = recordRefs.stream() + final Set huIdsToCheck = recordRefs.stream() .filter(recordRef -> I_M_HU.Table_Name.equals(recordRef.getTableName())) - .map(recordRef -> DocumentId.of(recordRef.getRecord_ID())) - .anyMatch(records::contains); + .map(recordRef -> recordRef.getRecord_ID()) + .collect(ImmutableSet.toImmutableSet()); + + final boolean containsSomeRecords = rowsBuffer.containsAnyOfHUIds(huIdsToCheck); if (!containsSomeRecords) { return; @@ -343,39 +301,22 @@ public void notifyRecordsChanged(final Set recordRefs) invalidateAll(); } - private IndexedHUEditorRows getRows() - { - return _recordsSupplier.get(); - } - - private IndexedHUEditorRows getRecordsNoLoad() - { - return _recordsSupplier.peek(); - } - - private IndexedHUEditorRows retrieveHUEditorRows() - { - final List rows = huEditorRepo.retrieveHUEditorRows(huIds); - return new IndexedHUEditorRows(rows); - } - @Override public Stream streamByIds(final Collection rowIds) { - return getRows().streamByIds(rowIds); + return rowsBuffer.streamByIds(rowIds); } /** @return top level rows and included rows recursive stream */ public Stream streamAllRecursive() { - return getRows().streamRecursive(); + return rowsBuffer.streamAllRecursive(); } @Override public List retrieveModelsByIds(final Collection rowIds, final Class modelClass) { - final Set huIds = getRows() - .streamByIds(rowIds) + final Set huIds = streamByIds(rowIds) .filter(HUEditorRow::isPureHU) .map(HUEditorRow::getM_HU_ID) .collect(GuavaCollectors.toImmutableSet()); @@ -396,7 +337,7 @@ public List retrieveModelsByIds(final Collection rowIds, fina @ViewAction(caption = "Barcode", layoutType = ProcessLayoutType.SingleOverlayField) public SelectViewRowsAction actionSelectHUsByBarcode( // @ViewActionParam(caption = "Barcode", widgetType = DocumentFieldWidgetType.Text) final String barcode // - //, final Set selectedRowIds // + // , final Set selectedRowIds // ) { // Search for matching rowIds by barcode @@ -422,97 +363,6 @@ public SelectViewRowsAction actionSelectHUsByBarcode( // } - // - // - // - private static final class IndexedHUEditorRows - { - /** Top level rows list */ - private final List rows; - /** All rows (included ones too) indexed by row Id */ - private final Map allRowsById; - - public IndexedHUEditorRows(final List rows) - { - super(); - this.rows = ImmutableList.copyOf(rows); - allRowsById = buildRowsByIdMap(this.rows); - } - - public HUEditorRow getById(final DocumentId rowId) - { - final HUEditorRow record = allRowsById.get(rowId); - if (record == null) - { - throw new EntityNotFoundException("No document found for rowId=" + rowId); - } - return record; - } - - public boolean contains(final DocumentId rowId) - { - return allRowsById.containsKey(rowId); - } - - public Stream streamByIds(final Collection rowIds) - { - if (rowIds == null || rowIds.isEmpty()) - { - return Stream.empty(); - } - - return rowIds.stream() - .distinct() - .map(rowId -> allRowsById.get(rowId)) - .filter(document -> document != null); - } - - public Stream stream() - { - return rows.stream(); - } - - public Stream streamRecursive() - { - return rows.stream() - .map(row -> streamRecursive(row)) - .reduce(Stream::concat) - .orElse(Stream.of()); - } - - private Stream streamRecursive(final HUEditorRow row) - { - return row.getIncludedDocuments() - .stream() - .map(includedRow -> streamRecursive(includedRow)) - .reduce(Stream.of(row), Stream::concat); - } - - public long size() - { - return rows.size(); - } - - private static ImmutableMap buildRowsByIdMap(final List records) - { - if (records.isEmpty()) - { - return ImmutableMap.of(); - } - - final ImmutableMap.Builder rowsById = ImmutableMap.builder(); - records.forEach(record -> indexByIdRecursively(rowsById, record)); - return rowsById.build(); - } - - private static final void indexByIdRecursively(final ImmutableMap.Builder collector, final HUEditorRow row) - { - collector.put(row.getDocumentId(), row); - row.getIncludedDocuments() - .forEach(includedRecord -> indexByIdRecursively(collector, includedRecord)); - } - } - // // // @@ -575,14 +425,6 @@ private Collection getHUIds() return huIds; } - private HUEditorViewRepository createHUEditorRepository() - { - return HUEditorViewRepository.builder() - .windowId(getViewId().getWindowId()) - .referencingTableName(referencingTableName) - .build(); - } - public Builder setReferencingDocumentPaths(final String referencingTableName, final Set referencingDocumentPaths) { this.referencingTableName = referencingTableName; @@ -590,6 +432,11 @@ public Builder setReferencingDocumentPaths(final String referencingTableName, fi return this; } + private String getReferencingTableName() + { + return referencingTableName; + } + private Set getReferencingDocumentPaths() { return referencingDocumentPaths == null ? ImmutableSet.of() : ImmutableSet.copyOf(referencingDocumentPaths); diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer.java new file mode 100644 index 000000000..356edab90 --- /dev/null +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer.java @@ -0,0 +1,62 @@ +package de.metas.ui.web.handlingunits; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +import de.metas.ui.web.exceptions.EntityNotFoundException; +import de.metas.ui.web.window.datatypes.DocumentId; +import de.metas.ui.web.window.model.DocumentQueryOrderBy; + +/* + * #%L + * metasfresh-webui-api + * %% + * Copyright (C) 2017 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * Buffer which contains all {@link HUEditorRow}s required for a given {@link HUEditorView} instance. + * + * Implementations of this interface are responsible for fetching the {@link HUEditorRow}s and (maybe)caching them. + * + * @author metas-dev + * + */ +public interface HUEditorViewBuffer +{ + long size(); + + void invalidateAll(); + + /** @return top level rows and included rows recursive stream */ + Stream streamAllRecursive(); + + Stream streamByIds(Collection rowIds); + + Stream streamPage(int firstRow, int pageLength, List orderBys); + + HUEditorRow getById(DocumentId rowId) throws EntityNotFoundException; + + boolean addHUIds(Collection huIdsToAdd); + + boolean removeHUIds(Collection huIdsToRemove); + + boolean containsAnyOfHUIds(Collection huIdsToCheck); + +} diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer_FullyCached.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer_FullyCached.java new file mode 100644 index 000000000..78e08f405 --- /dev/null +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewBuffer_FullyCached.java @@ -0,0 +1,265 @@ +package de.metas.ui.web.handlingunits; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Stream; + +import org.adempiere.util.lang.ExtendedMemorizingSupplier; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import de.metas.ui.web.exceptions.EntityNotFoundException; +import de.metas.ui.web.window.datatypes.DocumentId; +import de.metas.ui.web.window.model.DocumentQueryOrderBy; +import lombok.NonNull; + +/* + * #%L + * metasfresh-webui-api + * %% + * Copyright (C) 2017 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * {@link HUEditorViewBuffer} implementation which fully caches the {@link HUEditorRow}s. + * + * This implementation shall be used when dealing with small amount of HUs. + * + * @author metas-dev + * + */ +class HUEditorViewBuffer_FullyCached implements HUEditorViewBuffer +{ + private final HUEditorViewRepository huEditorRepo; + + private final CopyOnWriteArraySet huIds; + private final ExtendedMemorizingSupplier rowsSupplier = ExtendedMemorizingSupplier.of(() -> retrieveHUEditorRows()); + + HUEditorViewBuffer_FullyCached(@NonNull final HUEditorViewRepository huEditorRepo, final Collection huIds) + { + this.huEditorRepo = huEditorRepo; + this.huIds = new CopyOnWriteArraySet<>(huIds); + } + + private IndexedHUEditorRows getRows() + { + return rowsSupplier.get(); + } + + private IndexedHUEditorRows retrieveHUEditorRows() + { + final List rows = huEditorRepo.retrieveHUEditorRows(huIds); + return new IndexedHUEditorRows(rows); + } + + @Override + public long size() + { + return getRows().size(); + } + + @Override + public Stream streamAllRecursive() + { + return getRows().streamRecursive(); + } + + @Override + public Stream streamPage(final int firstRow, final int pageLength, final List orderBys) + { + Stream stream = getRows().stream() + .skip(firstRow) + .limit(pageLength); + + final Comparator comparator = createComparatorOrNull(orderBys); + if (comparator != null) + { + stream = stream.sorted(comparator); + } + + return stream; + } + + private static final Comparator createComparatorOrNull(final List orderBys) + { + if (orderBys == null || orderBys.isEmpty()) + { + return null; + } + + Comparator comparator = null; + for (final DocumentQueryOrderBy orderBy : orderBys) + { + final Comparator orderByComparator = orderBy. asComparator((row, fieldName) -> row.getFieldValueAsJson(fieldName)); + if (comparator == null) + { + comparator = orderByComparator; + } + else + { + comparator = comparator.thenComparing(orderByComparator); + } + } + + return comparator; + } + + @Override + public Stream streamByIds(final Collection rowIds) + { + return getRows().streamByIds(rowIds); + } + + @Override + public HUEditorRow getById(final DocumentId rowId) throws EntityNotFoundException + { + return getRows().getById(rowId); + } + + @Override + public void invalidateAll() + { + rowsSupplier.forget(); + huEditorRepo.invalidateAll(); + } + + @Override + public boolean addHUIds(final Collection huIdsToAdd) + { + if (huIdsToAdd.isEmpty()) + { + return false; + } + + return huIds.addAll(huIdsToAdd); + } + + @Override + public boolean removeHUIds(final Collection huIdsToRemove) + { + if (huIdsToRemove == null || huIdsToRemove.isEmpty()) + { + return false; + } + + return huIds.removeAll(huIdsToRemove); + } + + @Override + public boolean containsAnyOfHUIds(final Collection huIdsToCheck) + { + if (huIdsToCheck == null || huIdsToCheck.isEmpty()) + { + return false; + } + + return !Collections.disjoint(huIds, huIdsToCheck); + } + + // + // + // + private static final class IndexedHUEditorRows + { + /** Top level rows list */ + private final List rows; + /** All rows (included ones too) indexed by row Id */ + private final Map allRowsById; + + public IndexedHUEditorRows(final List rows) + { + super(); + this.rows = ImmutableList.copyOf(rows); + allRowsById = buildRowsByIdMap(this.rows); + } + + public HUEditorRow getById(final DocumentId rowId) + { + final HUEditorRow record = allRowsById.get(rowId); + if (record == null) + { + throw new EntityNotFoundException("No document found for rowId=" + rowId); + } + return record; + } + + public Stream streamByIds(final Collection rowIds) + { + if (rowIds == null || rowIds.isEmpty()) + { + return Stream.empty(); + } + + return rowIds.stream() + .distinct() + .map(rowId -> allRowsById.get(rowId)) + .filter(document -> document != null); + } + + public Stream stream() + { + return rows.stream(); + } + + public Stream streamRecursive() + { + return rows.stream() + .map(row -> streamRecursive(row)) + .reduce(Stream::concat) + .orElse(Stream.of()); + } + + private Stream streamRecursive(final HUEditorRow row) + { + return row.getIncludedDocuments() + .stream() + .map(includedRow -> streamRecursive(includedRow)) + .reduce(Stream.of(row), Stream::concat); + } + + public long size() + { + return rows.size(); + } + + private static ImmutableMap buildRowsByIdMap(final List records) + { + if (records.isEmpty()) + { + return ImmutableMap.of(); + } + + final ImmutableMap.Builder rowsById = ImmutableMap.builder(); + records.forEach(record -> indexByIdRecursively(rowsById, record)); + return rowsById.build(); + } + + private static final void indexByIdRecursively(final ImmutableMap.Builder collector, final HUEditorRow row) + { + collector.put(row.getDocumentId(), row); + row.getIncludedDocuments() + .forEach(includedRecord -> indexByIdRecursively(collector, includedRecord)); + } + } + +} diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java index 579993399..e64dcefe0 100644 --- a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java @@ -35,6 +35,7 @@ import de.metas.ui.web.window.datatypes.WindowId; import de.metas.ui.web.window.datatypes.json.JSONLookupValue; import lombok.Builder; +import lombok.NonNull; /* * #%L @@ -68,14 +69,14 @@ public class HUEditorViewRepository private final HUEditorRowAttributesProvider attributesProvider; @Builder - private HUEditorViewRepository(final WindowId windowId, final String referencingTableName) + private HUEditorViewRepository(@NonNull final WindowId windowId, final String referencingTableName, final HUEditorRowAttributesProvider attributesProvider) { super(); this.windowId = windowId; this.referencingTableName = referencingTableName; - this.attributesProvider = new HUEditorRowAttributesProvider(); + this.attributesProvider = attributesProvider; } public void invalidateAll()