Skip to content

Commit

Permalink
Add support for reload Actions (although conflict resolving is still …
Browse files Browse the repository at this point in the history
…broken)
  • Loading branch information
c24Felix committed Jul 3, 2024
1 parent 4a7cfb6 commit 1cd6092
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,11 @@ protected void addActionBean( Class<? extends ActionBean> clazz ) {
return;
}

getUrlBindingFactory().removeBinding(clazz);
// make sure mapping exists in cache
UrlBinding proto = getUrlBindingFactory().getBindingPrototype(clazz);
if ( proto == null ) {
getUrlBindingFactory().addBinding(clazz, new UrlBinding(clazz, binding));
getUrlBindingFactory().addBinding(clazz, new UrlBinding(clazz, binding), true);
}

// Construct the mapping of event->method for the class
Expand Down Expand Up @@ -471,7 +472,7 @@ protected Set<Class<? extends ActionBean>> findClasses() {
}

/** Provides subclasses with access to the configuration object. */
protected Configuration getConfiguration() { return _configuration; }
protected Configuration getConfiguration() {return _configuration;}

/**
* Looks to see if there is a single non-empty parameter value for the parameter name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,15 @@ public String getValue() {
}

/** Maps {@link ActionBean} classes to {@link UrlBinding}s */
private final Map<Class<? extends ActionBean>, UrlBinding> _classCache = new HashMap<>();
private final Map<Class<? extends ActionBean>, UrlBinding> _classPrimaryBindingCache = new HashMap<>();
/** Maps {@link ActionBean} classes to {@link UrlBinding}s */
private final Map<Class<? extends ActionBean>, List<UrlBinding>> _classBindingCache = new HashMap<>();
/** Maps simple paths to {@link UrlBinding}s */
private final Map<String, UrlBinding> _pathCache = new HashMap<>();
private final Map<String, UrlBinding> _pathCache = new HashMap<>();
/** Keeps a list of all the paths that could not be cached due to conflicts between URL bindings */
private final Map<String, List<UrlBinding>> _pathConflicts = new HashMap<>();
private final Map<String, List<UrlBinding>> _pathConflicts = new HashMap<>();
/** Holds the set of paths that are cached, sorted from longest to shortest */
private final Map<String, Set<UrlBinding>> _prefixCache = new TreeMap<>(new Comparator<>() {
private final Map<String, Set<UrlBinding>> _prefixCache = new TreeMap<>(new Comparator<>() {

@Override
public int compare( String a, String b ) {
Expand All @@ -233,24 +235,29 @@ public int compare( String a, String b ) {
* Map an {@link ActionBean} to a URL.
*
* @param beanType the {@link ActionBean} class
* @param binding the URL binding
* @param binding the URL binding
* @param isPrimary
*/
public void addBinding( Class<? extends ActionBean> beanType, UrlBinding binding ) {
public void addBinding( Class<? extends ActionBean> beanType, UrlBinding binding, boolean isPrimary ) {
// And now we can safely add the class
for ( String path : getCachedPaths(binding) ) {
cachePath(path, binding);
}
for ( String prefix : getCachedPrefixes(binding) ) {
cachePrefix(prefix, binding);
}
_classCache.put(beanType, binding);

_classBindingCache.computeIfAbsent(beanType, k -> new ArrayList<>()).add(binding);
if ( isPrimary ) {
_classPrimaryBindingCache.put(beanType, binding);
}
}

/**
* Get all the classes implementing {@link ActionBean}
*/
public Collection<Class<? extends ActionBean>> getActionBeanClasses() {
return Collections.unmodifiableSet(_classCache.keySet());
return Collections.unmodifiableSet(_classPrimaryBindingCache.keySet());
}

/**
Expand Down Expand Up @@ -356,7 +363,7 @@ public UrlBinding getBinding( HttpServletRequest request ) {
* @return a binding object if one is defined or null if not
*/
public UrlBinding getBindingPrototype( Class<? extends ActionBean> type ) {
UrlBinding primaryBinding = _classCache.get(type);
UrlBinding primaryBinding = _classPrimaryBindingCache.get(type);
if ( primaryBinding != null ) {
return primaryBinding;
}
Expand Down Expand Up @@ -498,62 +505,66 @@ public HashMap<String, Class<? extends ActionBean>> getPathMap() {
* @param beanType the {@link ActionBean} class
*/
public synchronized void removeBinding( Class<? extends ActionBean> beanType ) {
UrlBinding binding = _classCache.get(beanType);
if ( binding == null ) {
List<UrlBinding> urlBindings = _classBindingCache.get(beanType);
if ( urlBindings == null ) {
return;
}

Set<UrlBinding> resolvedConflicts = null;
for ( String path : getCachedPaths(binding) ) {
log.debug("Clearing cached path ", path, " for ", binding);
_pathCache.remove(path);
for ( UrlBinding binding : urlBindings ) {
Set<UrlBinding> resolvedConflicts = null;
for ( String path : getCachedPaths(binding) ) {
log.debug("Clearing cached path ", path, " for ", binding);
_pathCache.remove(path);

List<UrlBinding> conflicts = _pathConflicts.get(path);
if ( conflicts != null ) {
log.debug("Removing ", binding, " from conflicts list ", conflicts);
conflicts.remove(binding);
List<UrlBinding> conflicts = _pathConflicts.get(path);
if ( conflicts != null ) {
log.debug("Removing ", binding, " from conflicts list ", conflicts);
conflicts.remove(binding);

if ( conflicts.size() == 1 ) {
if ( resolvedConflicts == null ) {
resolvedConflicts = new LinkedHashSet<>();
}
if ( conflicts.size() == 1 ) {
if ( resolvedConflicts == null ) {
resolvedConflicts = new LinkedHashSet<>();
}

resolvedConflicts.add(_pathCache.get(conflicts.get(0)));
conflicts.clear();
}
// this cannot work since conflicts.get(0) returns an UrlBinding but PathCache is a Map<String, UrlBinding>
resolvedConflicts.add(_pathCache.get(conflicts.get(0)));
conflicts.clear();
}

if ( conflicts.isEmpty() ) {
_pathConflicts.remove(path);
if ( conflicts.isEmpty() ) {
_pathConflicts.remove(path);
}
}
}
}

for ( String prefix : getCachedPrefixes(binding) ) {
Set<UrlBinding> bindings = _prefixCache.get(prefix);
if ( bindings != null ) {
log.debug("Clearing cached prefix ", prefix, " for ", binding);
bindings.remove(binding);
if ( bindings.isEmpty() ) {
_prefixCache.remove(prefix);
for ( String prefix : getCachedPrefixes(binding) ) {
Set<UrlBinding> bindings = _prefixCache.get(prefix);
if ( bindings != null ) {
log.debug("Clearing cached prefix ", prefix, " for ", binding);
bindings.remove(binding);
if ( bindings.isEmpty() ) {
_prefixCache.remove(prefix);
}
}
}
}

_classCache.remove(beanType);

if ( resolvedConflicts != null ) {
log.debug("Resolved conflicts with ", resolvedConflicts);
if ( resolvedConflicts != null ) {
log.debug("Resolved conflicts with ", resolvedConflicts);

for ( UrlBinding conflict : resolvedConflicts ) {
removeBinding(conflict.getBeanType());
addBinding(conflict.getBeanType(), conflict);
for ( UrlBinding conflict : resolvedConflicts ) {
removeBinding(conflict.getBeanType());
addBinding(conflict.getBeanType(), conflict, true);
}
}
}

_classPrimaryBindingCache.remove(beanType);
_classBindingCache.remove(beanType);
}

@Override
public String toString() {
return String.valueOf(_classCache);
return String.valueOf(_classPrimaryBindingCache);
}

/**
Expand All @@ -574,13 +585,13 @@ protected UrlBinding addUrlBindings( Class<? extends ActionBean> beanType ) {

UrlBinding primaryBinding = parseUrlBinding(beanType, annotation.value());
if ( primaryBinding != null ) {
addBinding(beanType, primaryBinding);
addBinding(beanType, primaryBinding, true);
}

for ( String alternate : annotation.alternates() ) {
UrlBinding urlBinding = parseUrlBinding(beanType, alternate);
if ( urlBinding != null ) {
addBinding(beanType, urlBinding);
addBinding(beanType, urlBinding, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ public static void setupClass() {
@Test
public void testConflictDetectionIndependentOfClassLoadingOrder_failsRegardlessOfOrder() {
UrlBindingFactory factory = new UrlBindingFactory();
factory.addBinding(FooActionBean.class, parsePrimaryUrlBinding(FooActionBean.class));
factory.addBinding(FooActionBean2.class, parsePrimaryUrlBinding(FooActionBean.class));
factory.addBinding(FooActionBean.class, parsePrimaryUrlBinding(FooActionBean.class), true);
factory.addBinding(FooActionBean2.class, parsePrimaryUrlBinding(FooActionBean.class), true);

Throwable throwable = catchThrowable(() -> factory.getBindingPrototype("/foo"));

Expand Down

0 comments on commit 1cd6092

Please sign in to comment.