001// Copyright 2006, 2007, 2008, 2011, 2012 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.event; 016 017import java.util.ArrayList; 018import java.util.Collections; 019import java.util.HashSet; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023import java.util.function.Function; 024import java.util.stream.Collectors; 025 026import org.apache.tapestry5.commons.internal.util.TapestryException; 027import org.apache.tapestry5.commons.services.InvalidationEventHub; 028import org.apache.tapestry5.commons.services.InvalidationListener; 029import org.apache.tapestry5.commons.util.CollectionFactory; 030import org.slf4j.Logger; 031 032/** 033 * Base implementation class for classes (especially services) that need to manage a list of 034 * {@link org.apache.tapestry5.commons.services.InvalidationListener}s. 035 */ 036public class InvalidationEventHubImpl implements InvalidationEventHub 037{ 038 private final List<Function<List<String>, List<String>>> callbacks; 039 040 private final Logger logger; 041 042 protected InvalidationEventHubImpl(boolean productionMode, Logger logger) 043 { 044 if (productionMode) 045 { 046 callbacks = null; 047 } else 048 { 049 callbacks = CollectionFactory.newThreadSafeList(); 050 } 051 this.logger = logger; 052 } 053 054 /** 055 * Notifies all listeners/callbacks. 056 */ 057 protected final void fireInvalidationEvent() 058 { 059 fireInvalidationEvent(Collections.emptyList()); 060 } 061 062 /** 063 * Notifies all listeners/callbacks. 064 */ 065 public final void fireInvalidationEvent(List<String> resources) 066 { 067 if (callbacks == null) 068 { 069 return; 070 } 071 072 final Set<String> alreadyProcessed = new HashSet<>(); 073 074 int level = 1; 075 do 076 { 077 final Set<String> extraResources = new HashSet<>(); 078 Set<String> actuallyNewResources; 079 if (!resources.isEmpty()) 080 { 081 logger.info("Invalidating {} resource(s) at level {}: {}", resources.size(), level, String.join(", ", resources)); 082 } 083 else 084 { 085 logger.info("Invalidating all resources"); 086 } 087 for (Function<List<String>, List<String>> callback : callbacks) 088 { 089 final List<String> newResources = callback.apply(resources); 090 if (newResources == null) { 091 throw new TapestryException("InvalidationEventHub callback functions cannot return null", null); 092 } 093 actuallyNewResources = newResources.stream() 094 .filter(r -> !alreadyProcessed.contains(r)) 095 .collect(Collectors.toSet()); 096 extraResources.addAll(actuallyNewResources); 097 alreadyProcessed.addAll(newResources); 098 } 099 level++; 100 resources = new ArrayList<>(extraResources); 101 } 102 while (!resources.isEmpty()); 103 } 104 105 public final void addInvalidationCallback(final Runnable callback) 106 { 107 assert callback != null; 108 109 // In production mode, callbacks may be null, in which case, just 110 // ignore the callback. 111 if (callbacks != null) 112 { 113 callbacks.add((r) -> { 114 callback.run(); 115 return Collections.emptyList(); 116 }); 117 } 118 } 119 120 public final void clearOnInvalidation(final Map<?, ?> map) 121 { 122 assert map != null; 123 124 addInvalidationCallback(new Runnable() 125 { 126 public void run() 127 { 128 map.clear(); 129 } 130 }); 131 } 132 133 public final void addInvalidationListener(final InvalidationListener listener) 134 { 135 assert listener != null; 136 137 addInvalidationCallback(new Runnable() 138 { 139 public void run() 140 { 141 listener.objectWasInvalidated(); 142 } 143 }); 144 } 145 146 @Override 147 public void addInvalidationCallback(Function<List<String>, List<String>> callback) 148 { 149 if (callbacks != null) 150 { 151 callbacks.add(callback); 152 } 153 } 154 155}