/***************************************************************** * 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.cayenne; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.cayenne.cache.QueryCacheEntryFactory; import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.RefreshQuery; import org.apache.cayenne.reflect.AttributeProperty; import org.apache.cayenne.reflect.ClassDescriptor; import org.apache.cayenne.reflect.PropertyVisitor; import org.apache.cayenne.reflect.ToManyProperty; import org.apache.cayenne.reflect.ToOneProperty; import org.apache.cayenne.remote.RemoteIncrementalFaultList; import org.apache.cayenne.util.ListResponse; import org.apache.cayenne.util.ObjectContextQueryAction; /** * @since 1.2 */ class CayenneContextQueryAction extends ObjectContextQueryAction { CayenneContextQueryAction(CayenneContext actingContext, ObjectContext targetContext, Query query) { super(actingContext, targetContext, query); } @Override protected boolean interceptPaginatedQuery() { if (metadata.getPageSize() > 0) { response = new ListResponse(new RemoteIncrementalFaultList( actingContext, query)); return DONE; } return !DONE; } @Override protected QueryCacheEntryFactory getCacheObjectFactory() { return new QueryCacheEntryFactory() { public List createObject() { if (interceptPaginatedQuery() != DONE) { runQuery(); } return response.firstList(); } }; } @Override protected boolean interceptRefreshQuery() { if (query instanceof RefreshQuery) { RefreshQuery refreshQuery = (RefreshQuery) query; CayenneContext context = (CayenneContext) actingContext; // handle 4 separate scenarios, but do not combine them as it will be // unclear how to handle cascading behavior // 1. refresh all if (refreshQuery.isRefreshAll()) { invalidateLocally(context.internalGraphManager(), context .internalGraphManager() .registeredNodes() .iterator()); context.getQueryCache().clear(); // cascade return !DONE; } // 2. invalidate object collection Collection<?> objects = refreshQuery.getObjects(); if (objects != null && !objects.isEmpty()) { invalidateLocally(context.internalGraphManager(), objects.iterator()); // cascade return !DONE; } // 3. refresh query - have to do it eagerly to refresh the objects involved if (refreshQuery.getQuery() != null) { Query cachedQuery = refreshQuery.getQuery(); String cacheKey = cachedQuery .getMetaData(context.getEntityResolver()) .getCacheKey(); context.getQueryCache().remove(cacheKey); this.response = context.performGenericQuery(cachedQuery); // do not cascade to avoid running query twice return DONE; } // 4. refresh groups... if (refreshQuery.getGroupKeys() != null && refreshQuery.getGroupKeys().length > 0) { String[] groups = refreshQuery.getGroupKeys(); for (String group : groups) { context.getQueryCache().removeGroup(group); } // cascade group invalidation return !DONE; } } return !DONE; } private void invalidateLocally(CayenneContextGraphManager graphManager, Iterator<?> it) { if (!it.hasNext()) { return; } EntityResolver resolver = actingContext.getEntityResolver(); while (it.hasNext()) { final Persistent object = (Persistent) it.next(); // we don't care about NEW objects, // but we still do care about HOLLOW, since snapshot might still be // present if (object.getPersistenceState() == PersistenceState.NEW) { continue; } ObjectId id = object.getObjectId(); // per CAY-1082 ROP objects (unlike CayenneDataObject) require all // relationship faults invalidation. ClassDescriptor descriptor = resolver.getClassDescriptor(id.getEntityName()); PropertyVisitor arcInvalidator = new PropertyVisitor() { public boolean visitAttribute(AttributeProperty property) { return true; } public boolean visitToMany(ToManyProperty property) { property.invalidate(object); return true; } public boolean visitToOne(ToOneProperty property) { property.invalidate(object); return true; } }; descriptor.visitProperties(arcInvalidator); object.setPersistenceState(PersistenceState.HOLLOW); // remove cached changes graphManager.changeLog.unregisterNode(id); graphManager.stateLog.unregisterNode(id); } } }