001/* 002 * (C) Copyright 2006-2017 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 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.storage.dbs; 020 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.util.Collection; 026import java.util.HashSet; 027import java.util.Set; 028 029import org.nuxeo.ecm.core.pubsub.SerializableInvalidations; 030 031/** 032 * A set of invalidations for a given repository. 033 * <p> 034 * Records both modified and deleted fragments, as well as "parents modified" fragments. 035 * 036 * @since 8.10 037 */ 038public class DBSInvalidations implements SerializableInvalidations { 039 040 private static final long serialVersionUID = 1L; 041 042 /** 043 * Maximum number of invalidations kept, after which only {@link #all} is set. This avoids accumulating too many 044 * invalidations in memory, at the expense of more coarse-grained invalidations. 045 */ 046 public static final int MAX_SIZE = 10000; 047 048 /** 049 * Used locally when invalidating everything, or when too many invalidations have been received. 050 */ 051 public boolean all; 052 053 /** null when empty */ 054 public Set<String> ids; 055 056 public DBSInvalidations() { 057 } 058 059 public DBSInvalidations(boolean all) { 060 this.all = all; 061 } 062 063 @Override 064 public boolean isEmpty() { 065 return ids == null && !all; 066 } 067 068 public void clear() { 069 all = false; 070 ids = null; 071 } 072 073 protected void setAll() { 074 all = true; 075 ids = null; 076 } 077 078 protected void checkMaxSize() { 079 if (ids != null && ids.size() > MAX_SIZE) { 080 setAll(); 081 } 082 } 083 084 @Override 085 public void add(SerializableInvalidations o) { 086 DBSInvalidations other = (DBSInvalidations) o; 087 if (other == null) { 088 return; 089 } 090 if (all) { 091 return; 092 } 093 if (other.all) { 094 setAll(); 095 return; 096 } 097 if (other.ids != null) { 098 if (ids == null) { 099 ids = new HashSet<>(); 100 } 101 ids.addAll(other.ids); 102 } 103 checkMaxSize(); 104 } 105 106 public void add(String id) { 107 if (all) { 108 return; 109 } 110 if (ids == null) { 111 ids = new HashSet<>(); 112 } 113 ids.add(id); 114 checkMaxSize(); 115 } 116 117 public void addAll(Collection<String> idsToAdd) { 118 if (all) { 119 return; 120 } 121 if (ids == null) { 122 ids = new HashSet<>(idsToAdd); 123 } else { 124 ids.addAll(idsToAdd); 125 } 126 checkMaxSize(); 127 } 128 129 private static final String UTF_8 = "UTF-8"; 130 131 private static final int ALL_IDS = (byte) 'A'; 132 133 private static final int ID_SEP = (byte) ','; 134 135 @Override 136 public void serialize(OutputStream out) throws IOException { 137 if (all) { 138 out.write(ALL_IDS); 139 } else if (ids != null) { 140 for (String id : ids) { 141 out.write(ID_SEP); 142 out.write(id.getBytes(UTF_8)); 143 } 144 } 145 } 146 147 public static DBSInvalidations deserialize(InputStream in) throws IOException { 148 int first = in.read(); 149 if (first == -1) { 150 // empty message 151 return null; 152 } 153 DBSInvalidations invalidations = new DBSInvalidations(); 154 if (first == ALL_IDS) { 155 invalidations.setAll(); 156 } else if (first != ID_SEP) { 157 // invalid message 158 return null; 159 } else { 160 ByteArrayOutputStream baout = new ByteArrayOutputStream(36); // typical uuid size 161 for (;;) { 162 int b = in.read(); // we read from a ByteArrayInputStream so one at a time is ok 163 if (b == ID_SEP || b == -1) { 164 invalidations.add(baout.toString(UTF_8)); 165 if (b == -1) { 166 break; 167 } 168 baout.reset(); 169 } else { 170 baout.write(b); 171 } 172 } 173 } 174 return invalidations; 175 } 176 177 @Override 178 public String toString() { 179 StringBuilder sb = new StringBuilder(this.getClass().getSimpleName() + '('); 180 if (all) { 181 sb.append("all=true"); 182 } 183 if (ids != null) { 184 sb.append("ids="); 185 sb.append(ids); 186 } 187 sb.append(')'); 188 return sb.toString(); 189 } 190 191}