001 /* 002 Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.aspects.integrity; 019 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Set; 023 import java.util.Vector; 024 import org.aopalliance.intercept.MethodInvocation; 025 import org.apache.log4j.Logger; 026 import org.objectweb.jac.core.AspectComponent; 027 import org.objectweb.jac.core.Collaboration; 028 import org.objectweb.jac.core.Wrapper; 029 import org.objectweb.jac.core.rtti.CollectionItem; 030 import org.objectweb.jac.core.rtti.FieldItem; 031 import org.objectweb.jac.core.rtti.MethodItem; 032 import org.objectweb.jac.core.rtti.RttiAC; 033 import org.objectweb.jac.util.Strings; 034 035 /** 036 * This aspect handle different kinds of data integrity among objects 037 * sets. */ 038 039 public class IntegrityAC extends AspectComponent implements IntegrityConf { 040 static final Logger loggerRep = Logger.getLogger("integrity.repository"); 041 042 HashMap preConditionsFields = new HashMap(); 043 HashMap postConditionsFields = new HashMap(); 044 045 RoleWrapper roleWrapper = new RoleWrapper(this); 046 047 public static final String CONSTRAINTS = "IntegrityAC.CONSTRAINTS"; 048 public static final String DISABLE_ROLE_UPDATES = 049 "IntegrityAC.DISABLE_ROLE_UPDATES"; 050 051 public static final String DISABLE_ADD_TO_REPOSITORY = 052 "IntegrityAC.DISABLE_ADD_TO_REPOSITORY"; 053 054 public void declareRepository( 055 String repositoryName, 056 CollectionItem collection, 057 FieldItem field) 058 { 059 RepositoryWrapper rw = 060 new RepositoryWrapper( 061 this, 062 repositoryName, 063 collection, 064 field, 065 RepositoryWrapper.ADDER); 066 if (field instanceof CollectionItem) { 067 pointcut( 068 "ALL", 069 field.getParent().getName(), 070 "ADDERS(" + field.getName() + ")", 071 rw, 072 null); 073 } else { 074 pointcut( 075 "ALL", 076 field.getParent().getName(), 077 "SETTERS(" + field.getName() + ")", 078 rw, 079 null); 080 } 081 082 // This pointcut adds an attribute in context so that we can 083 // avoid adding an object twice in the repository 084 pointcut(repositoryName,collection.getParent().getName(), 085 "ADDERS("+collection.getName()+")", 086 new AntiDupWrapper(this,collection),null); 087 } 088 089 class AntiDupWrapper extends Wrapper { 090 public AntiDupWrapper(AspectComponent ac, CollectionItem collection) { 091 super(ac); 092 this.collection = collection; 093 } 094 CollectionItem collection; 095 public Object invoke(MethodInvocation invocation) throws Throwable { 096 disableAddToRepository(collection); 097 loggerRep.debug("doAddToRepository " + collection); 098 try { 099 return proceed(invocation); 100 } finally { 101 enableAddToRepository(collection); 102 } 103 } 104 } 105 106 public void declareConstraint( 107 FieldItem relation, 108 FieldItem target, 109 String constraint) 110 { 111 List constraints = (List) target.getAttributeAlways(CONSTRAINTS); 112 if (constraints == null) { 113 constraints = new Vector(); 114 target.setAttribute(CONSTRAINTS, constraints); 115 } 116 int c; 117 if (constraint.compareToIgnoreCase("DELETE_CASCADE") == 0) { 118 warning("DELETE_CASCADE is not implemented yet"); 119 c = Constraint.DELETE_CASCADE; 120 } else if (constraint.compareToIgnoreCase("SET_NULL") == 0) { 121 c = Constraint.SET_NULL; 122 } else if (constraint.compareToIgnoreCase("FORBIDDEN") == 0) { 123 c = Constraint.FORBIDDEN; 124 } else { 125 throw new RuntimeException("Unknown constraint type " + constraint); 126 } 127 constraints.add(new Constraint(relation, c)); 128 if (target instanceof CollectionItem) { 129 pointcut( 130 "ALL", 131 target.getParent().getName(), 132 "REMOVERS(" + target.getName() + ")", 133 new RepositoryWrapper( 134 this, 135 "", 136 (CollectionItem) target, 137 null, 138 RepositoryWrapper.REMOVER), 139 null); 140 } else { 141 pointcut( 142 "ALL", 143 target.getParent().getName(), 144 "SETTERS(" + target.getName() + ")", 145 new RepositoryWrapper( 146 this, 147 "", 148 null, 149 target, 150 RepositoryWrapper.REMOVER), 151 null); 152 } 153 } 154 155 boolean hasConditions = false; 156 157 private void addCondition( 158 FieldItem field, 159 MethodItem constraint, 160 Object[] params, 161 String errorMsg, 162 HashMap conditionsList) 163 { 164 hasConditions = true; 165 field.setAttribute(RttiAC.CONSTRAINTS, Boolean.TRUE); 166 167 Vector vect = (Vector) conditionsList.get(field); 168 if (vect == null) 169 vect = new Vector(); 170 171 ConstraintDescriptor obj = 172 new ConstraintDescriptor(constraint, params, errorMsg); 173 vect.add(obj); 174 conditionsList.put(field, vect); 175 } 176 177 public void addPostCondition( 178 FieldItem field, 179 MethodItem constraint, 180 Object[] params, 181 String errorMsg) 182 { 183 addCondition( 184 field, 185 constraint, 186 params, 187 errorMsg, 188 postConditionsFields); 189 } 190 191 public void addPreCondition( 192 FieldItem field, 193 MethodItem constraint, 194 Object[] params, 195 String errorMsg) 196 { 197 addCondition( 198 field, 199 constraint, 200 params, 201 errorMsg, 202 preConditionsFields); 203 } 204 205 /** 206 * Activates constraints checking. 207 * 208 * <p>This method must be called once after all calls to 209 * <code>addPreCondition</code> and <code>addPostCondition</code> 210 * to effectively add pointcuts for these methods.</p> 211 * 212 * @see #addPreCondition(FieldItem,MethodItem,Object[],String) 213 * @see #addPostCondition(FieldItem,MethodItem,Object[],String) 214 */ 215 protected void doCheck() { 216 ConstraintWrapper cw = new ConstraintWrapper(this); 217 pointcut( 218 "ALL", 219 "ALL", 220 "SETTERS(<" + RttiAC.CONSTRAINTS + ">)", 221 cw, 222 null); 223 } 224 225 /** 226 * Activates primary keys checking. 227 * 228 * <p>This method must be called once in the file integrity.acc to 229 * effectively add pointcuts for primary key checking methods.</p> 230 * 231 * @see org.objectweb.jac.core.rtti.RttiAC#definePrimaryKey(ClassItem,String,String[]) 232 */ 233 void doPrimaryKeyCheck() { 234 PrimaryKeyWrapper pw = new PrimaryKeyWrapper(this); 235 pointcut( 236 "ALL", 237 "ALL", 238 "ADDERS(<" + RttiAC.PRIMARY_KEY + ">)", 239 pw, 240 null); 241 } 242 243 boolean updateAssociations = false; 244 public void updateAssociations() { 245 this.updateAssociations = true; 246 } 247 248 public void whenConfigured() { 249 if (!updateAssociations) 250 return; 251 if (hasConditions) { 252 doCheck(); 253 } 254 doPrimaryKeyCheck(); 255 Set classes = ((RttiAC)getAC("rtti")).getClassesWithAssociations(); 256 if (!classes.isEmpty()) { 257 String classExpr = Strings.join(classes, " || "); 258 pointcut( 259 "ALL", 260 classExpr, 261 "ADDERS(<" + RttiAC.OPPOSITE_ROLE + ">) " 262 + "|| REMOVERS(<" + RttiAC.OPPOSITE_ROLE + ">) " 263 + "|| SETTERS(<" + RttiAC.OPPOSITE_ROLE + ">) " 264 + "|| CONSTRUCTORS", 265 roleWrapper, 266 null); 267 //pointcut("ALL",classExpr,"CONSTRUCTORS", 268 // roleWrapper,null); 269 } 270 } 271 272 /** 273 * Tells wether adding objects to a repository collection is enabled or not. 274 * @param collection the repository collection to test 275 */ 276 public static boolean isAddToRepositoryEnabled(CollectionItem collection) { 277 List disabled = getDisabled(); 278 loggerRep.debug("isAddToRepositoryEnabled "+collection+" (disabled="+disabled+") ?"); 279 return !disabled.contains(collection); 280 } 281 282 /** 283 * Disables adding objects to a repository collection. 284 * @param collection the repository collection to disable 285 */ 286 public static void disableAddToRepository(CollectionItem collection) { 287 Collaboration collab = Collaboration.get(); 288 List disabled = getDisabled(); 289 loggerRep.debug("disableAddToRepository "+collection+ 290 " (disabled="+disabled+")"); 291 disabled.add(collection); 292 } 293 294 /** 295 * Enables adding objects to a repository collection. 296 * @param collection the repository collection to enable 297 */ 298 public static void enableAddToRepository(CollectionItem collection) { 299 loggerRep.debug("enableAddToRepository "+collection); 300 List disabled = getDisabled(); 301 disabled.remove(collection); 302 } 303 304 /** 305 * Gets the list of disabled repository 306 */ 307 protected static List getDisabled() { 308 List disabled = 309 (List)Collaboration.get().getAttribute( 310 IntegrityAC.DISABLE_ADD_TO_REPOSITORY); 311 if (disabled==null) { 312 disabled = new Vector(); 313 Collaboration.get().addAttribute( 314 IntegrityAC.DISABLE_ADD_TO_REPOSITORY, 315 disabled); 316 } 317 return disabled; 318 } 319 320 public static void disableRoleUpdates() { 321 Collaboration.get().addAttribute(DISABLE_ROLE_UPDATES,Boolean.TRUE); 322 } 323 public static void enableRoleUpdates() { 324 Collaboration.get().removeAttribute(DISABLE_ROLE_UPDATES); 325 } 326 }