001package io.ebeaninternal.server.autotune.service; 002 003import io.ebean.bean.NodeUsageCollector; 004import io.ebean.text.PathProperties; 005import io.ebean.util.SplitName; 006import io.ebeaninternal.server.deploy.BeanDescriptor; 007import io.ebeaninternal.server.deploy.BeanProperty; 008import io.ebeaninternal.server.deploy.BeanPropertyAssoc; 009import io.ebeaninternal.server.el.ElPropertyValue; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import java.util.LinkedHashSet; 014import java.util.Set; 015import java.util.concurrent.locks.ReentrantLock; 016 017/** 018 * Collects usages statistics for a given node in the object graph. 019 */ 020public class ProfileOriginNodeUsage { 021 022 private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class); 023 024 private final ReentrantLock lock = new ReentrantLock(); 025 026 private final String path; 027 028 private int profileCount; 029 030 private int profileUsedCount; 031 032 private boolean modified; 033 034 private final Set<String> aggregateUsed = new LinkedHashSet<>(); 035 036 public ProfileOriginNodeUsage(String path) { 037 // handle null paths as using ConcurrentHashMap 038 this.path = "".equals(path) ? null : path; 039 } 040 041 protected void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc, boolean addVersionProperty) { 042 lock.lock(); 043 try { 044 BeanDescriptor<?> desc = rootDesc; 045 if (path != null) { 046 ElPropertyValue elGetValue = rootDesc.getElGetValue(path); 047 if (elGetValue == null) { 048 logger.warn("AutoTune: Can't find join for path[" + path + "] for " + rootDesc.getName()); 049 return; 050 } else { 051 BeanProperty beanProperty = elGetValue.getBeanProperty(); 052 if (beanProperty instanceof BeanPropertyAssoc<?>) { 053 desc = ((BeanPropertyAssoc<?>) beanProperty).getTargetDescriptor(); 054 } 055 } 056 } 057 058 BeanProperty toOneIdProperty = null; 059 boolean addedToPath = false; 060 061 for (String propName : aggregateUsed) { 062 BeanProperty beanProp = desc.findPropertyFromPath(propName); 063 if (beanProp == null) { 064 logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName()); 065 066 } else { 067 if (beanProp.isId()) { 068 // remember and maybe add ToOne property to parent path 069 toOneIdProperty = beanProp; 070 } else if (beanProp instanceof BeanPropertyAssoc<?>) { 071 // intentionally skip 072 } else { 073 //noinspection StatementWithEmptyBody 074 if (beanProp.isLob() && !beanProp.isFetchEager()) { 075 // AutoTune will not include Lob's marked FetchLazy 076 // (which is the default for Lob's so typical). 077 } else { 078 addedToPath = true; 079 pathProps.addToPath(path, beanProp.getName()); 080 } 081 } 082 } 083 } 084 085 if ((modified || addVersionProperty) && desc != null) { 086 BeanProperty versionProp = desc.getVersionProperty(); 087 if (versionProp != null) { 088 addedToPath = true; 089 pathProps.addToPath(path, versionProp.getName()); 090 } 091 } 092 093 if (toOneIdProperty != null && !addedToPath) { 094 // add ToOne property to parent path 095 ElPropertyValue assocOne = rootDesc.getElGetValue(path); 096 pathProps.addToPath(SplitName.parent(path), assocOne.getName()); 097 } 098 } finally { 099 lock.unlock(); 100 } 101 } 102 103 /** 104 * Collect usage from a node. 105 */ 106 protected void collectUsageInfo(NodeUsageCollector profile) { 107 lock.lock(); 108 try { 109 Set<String> used = profile.getUsed(); 110 111 profileCount++; 112 if (!used.isEmpty()) { 113 profileUsedCount++; 114 aggregateUsed.addAll(used); 115 } 116 if (profile.isModified()) { 117 modified = true; 118 } 119 } finally { 120 lock.unlock(); 121 } 122 } 123 124 @Override 125 public String toString() { 126 return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount + "] props" + aggregateUsed; 127 } 128}