ItEr10S08AdministracionGruposItEr09S09: Avoided the creation of a dumb controller for doing the redirections, this controller is synthesized instead.

This commit is contained in:
Óscar González Fernández 2009-05-27 18:22:17 +02:00 committed by Javier Moran Rua
parent 9b3fa481d7
commit 6e80beb367
10 changed files with 227 additions and 121 deletions

View file

@ -13,6 +13,6 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Linkable {
public @interface LinkToState {
public String[] value();
}

View file

@ -11,9 +11,9 @@ import org.springframework.beans.factory.annotation.Qualifier;
* Marks a controller that redirects to the real controller <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
@Qualifier
@Qualifier("linked")
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
public @interface Redirecter {
public @interface Linked {
}

View file

@ -10,8 +10,10 @@ import java.lang.annotation.RetentionPolicy;
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Page {
public @interface LinksDefiner {
public String value();
public String page();
public String beanName();
}

View file

@ -11,6 +11,8 @@ import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.zk.ui.Execution;
/**
@ -19,12 +21,14 @@ import org.zkoss.zk.ui.Execution;
*/
public class Redirector<T> {
private static class LinkableMetadata {
private static final Log LOG = LogFactory.getLog(Redirector.class);
private static class LinkMetadata {
private final Method method;
private final Linkable annotation;
private final LinkToState annotation;
private LinkableMetadata(Method method, Linkable annotation) {
private LinkMetadata(Method method, LinkToState annotation) {
this.method = method;
this.annotation = annotation;
}
@ -32,57 +36,56 @@ public class Redirector<T> {
private final ExecutorRetriever executorRetriever;
private Map<String, LinkableMetadata> metadata = new HashMap<String, LinkableMetadata>();
private Map<String, LinkMetadata> metadata = new HashMap<String, LinkMetadata>();
private final String page;
private final IConverterFactory converterFactory;
public Redirector(IConverterFactory converterFactory,
ExecutorRetriever executorRetriever,
Class<T> klassWithLinkableMetadata) {
ExecutorRetriever executorRetriever, Class<T> interfaceDefiningLinks) {
Validate.isTrue(interfaceDefiningLinks.isInterface());
this.converterFactory = converterFactory;
this.executorRetriever = executorRetriever;
Page pageAnnotation = klassWithLinkableMetadata
.getAnnotation(Page.class);
Validate.notNull(pageAnnotation, Page.class.getName()
+ " annotation required on "
+ klassWithLinkableMetadata.getName());
this.page = pageAnnotation.value();
for (Method method : klassWithLinkableMetadata.getMethods()) {
Linkable linkable = method.getAnnotation(Linkable.class);
if (linkable != null) {
metadata.put(method.getName(), new LinkableMetadata(method,
linkable));
LinksDefiner linkDefiner = interfaceDefiningLinks
.getAnnotation(LinksDefiner.class);
Validate
.notNull(linkDefiner, LinksDefiner.class.getName()
+ " annotation required on "
+ interfaceDefiningLinks.getName());
this.page = linkDefiner.page();
for (Method method : interfaceDefiningLinks.getMethods()) {
LinkToState linkToState = method.getAnnotation(LinkToState.class);
if (linkToState != null) {
metadata.put(method.getName(), new LinkMetadata(method,
linkToState));
}
}
}
public void doRedirect(Object... values) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement invoker = stackTrace[2];
String methodName = invoker.getMethodName();
public void doRedirect(String methodName, Object... values) {
if (!metadata.containsKey(methodName)) {
throw new RuntimeException(
"It's not invoked on a method in which there is linkable information. Method is: "
+ methodName);
LOG.error("Method " + methodName
+ "doesn't represent a state(It doesn't have a "
+ LinkToState.class.getSimpleName()
+ " annotation). Nothing will be done");
return;
}
LinkableMetadata linkableMetadata = metadata.get(methodName);
LinkMetadata linkableMetadata = metadata.get(methodName);
Class<?>[] types = linkableMetadata.method.getParameterTypes();
int i = 0;
String[] parameterNames = linkableMetadata.annotation.value();
String[] associatedValues = new String[parameterNames.length];
for (Class<?> type : types) {
String[] stringRepresentations = new String[parameterNames.length];
for (int i = 0; i < types.length; i++) {
Class<?> type = types[i];
Converter<?> converterFor = converterFactory.getConverterFor(type);
associatedValues[i] = converterFor.asStringUngeneric(values[i]);
i++;
stringRepresentations[i] = converterFor
.asStringUngeneric(values[i]);
}
StringBuilder linkValue = new StringBuilder(page);
for (int j = 0; j < parameterNames.length; j++) {
String value = associatedValues[j];
linkValue.append(";").append(parameterNames[j]);
if (value != null)
linkValue.append("=").append(value);
for (int i = 0; i < parameterNames.length; i++) {
linkValue.append(";").append(parameterNames[i]);
if (stringRepresentations[i] != null)
linkValue.append("=").append(stringRepresentations[i]);
}
executorRetriever.getCurrent().sendRedirect(linkValue.toString());
}
@ -104,25 +107,31 @@ public class Redirector<T> {
Map<String, String> matrixParams = MatrixParameters
.extract((HttpServletRequest) current.getNativeRequest());
Set<String> matrixParamsNames = matrixParams.keySet();
for (Entry<String, LinkableMetadata> entry : metadata.entrySet()) {
LinkableMetadata linkableMetadata = entry.getValue();
Linkable annotation = linkableMetadata.annotation;
for (Entry<String, LinkMetadata> entry : metadata.entrySet()) {
LinkMetadata linkMetadata = entry.getValue();
LinkToState linkToStateAnnotation = linkMetadata.annotation;
HashSet<String> requiredParams = new HashSet<String>(Arrays
.asList(annotation.value()));
.asList(linkToStateAnnotation.value()));
if (matrixParamsNames.equals(requiredParams)) {
Class<?>[] parameterTypes = linkableMetadata.method
.getParameterTypes();
Object[] arguments = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Object argumentName = annotation.value()[i];
String parameterValue = matrixParams.get(argumentName);
Converter<?> converter = converterFactory
.getConverterFor(parameterTypes[i]);
arguments[i] = converter.asObject(parameterValue);
}
callMethod(controller, linkableMetadata.method, arguments);
Object[] arguments = retrieveArguments(matrixParams,
linkToStateAnnotation, linkMetadata.method
.getParameterTypes());
callMethod(controller, linkMetadata.method, arguments);
return;
}
}
}
private Object[] retrieveArguments(Map<String, String> matrixParams,
LinkToState linkToStateAnnotation, Class<?>[] parameterTypes) {
Object[] result = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Object argumentName = linkToStateAnnotation.value()[i];
String parameterValue = matrixParams.get(argumentName);
Converter<?> converter = converterFactory
.getConverterFor(parameterTypes[i]);
result[i] = converter.asObject(parameterValue);
}
return result;
}
}

View file

@ -0,0 +1,147 @@
package org.navalplanner.web.common;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;
/**
* Creates implemnetations of controllers that sends http redirects to the
* proper page <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class RedirectorSynthetiser implements BeanFactoryPostProcessor {
private static final Log LOG = LogFactory
.getLog(RedirectorSynthetiser.class);
private final class SynthetizedImplementation implements InvocationHandler {
private final ConfigurableListableBeanFactory beanFactory;
private final Class<?> pageInterface;
private Redirector<?> redirector;
private SynthetizedImplementation(
ConfigurableListableBeanFactory beanFactory,
Class<?> pageInterface) {
this.beanFactory = beanFactory;
this.pageInterface = pageInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Redirector<?> redirector = getRedirector();
redirector.doRedirect(method.getName(), args);
return null;
}
private Redirector<?> getRedirector() {
if (redirector != null)
return redirector;
RedirectorRegistry redirectorRegistry = (RedirectorRegistry) beanFactory
.getBean(getSpringDefaultName(RedirectorRegistry.class),
RedirectorRegistry.class);
redirector = redirectorRegistry.getRedirectorFor(pageInterface);
return redirector;
}
}
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
long elapsedTime = System.currentTimeMillis();
for (Class<?> pageInterface : findInterfacesMarkedWithLinkable()) {
beanFactory.registerSingleton(getBeanName(pageInterface),
createRedirectorImplementationFor(beanFactory,
pageInterface));
}
elapsedTime = System.currentTimeMillis() - elapsedTime;
LOG.debug("Took " + elapsedTime
+ " ms to search for interfaces annotated with "
+ LinksDefiner.class.getSimpleName());
}
private List<Class<?>> findInterfacesMarkedWithLinkable() {
List<Class<?>> result = new ArrayList<Class<?>>();
PathMatchingResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
resourceResolver);
for (Resource resource : findResourcesCouldMatch(resourceResolver)) {
addIfSuitable(result, metadataReaderFactory, resource);
}
return result;
}
private Resource[] findResourcesCouldMatch(
PathMatchingResourcePatternResolver resourceResolver) {
try {
return resourceResolver
.getResources("classpath*:"
+ ClassUtils
.convertClassNameToResourcePath("org.navalplanner.web")
+ "/" + "**/*.class");
} catch (IOException e) {
throw new RuntimeException("couldn't load any resource", e);
}
}
private void addIfSuitable(List<Class<?>> accumulatedResult,
CachingMetadataReaderFactory metadataReaderFactory,
Resource resource) {
try {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory
.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = metadataReader
.getAnnotationMetadata();
ClassMetadata classMetadata = metadataReader.getClassMetadata();
if (classMetadata.isInterface()
&& annotationMetadata.getAnnotationTypes().contains(
LinksDefiner.class.getName())) {
Class<?> klass = Class
.forName(classMetadata.getClassName());
if (klass.isInterface()) {
accumulatedResult.add(klass);
}
}
}
} catch (Exception e) {
LOG.warn("exception processing " + resource, e);
}
}
private Object createRedirectorImplementationFor(
final ConfigurableListableBeanFactory beanFactory,
final Class<?> pageInterface) {
return Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { pageInterface }, new SynthetizedImplementation(
beanFactory, pageInterface));
}
private static String getSpringDefaultName(Class<?> klass) {
String simpleName = klass.getSimpleName();
return simpleName.substring(0, 1).toLowerCase()
+ simpleName.substring(1);
}
private static String getBeanName(Class<?> pageInterface) {
LinksDefiner annotation = pageInterface.getAnnotation(LinksDefiner.class);
return annotation.beanName();
}
}

View file

@ -9,7 +9,7 @@ import java.util.Set;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.Util;
import org.navalplanner.web.resources.worker.IWorkerCRUDController;
import org.navalplanner.web.resources.worker.WorkerCRUDLinks;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
@ -34,7 +34,7 @@ public class CriterionWorkersController extends GenericForwardComposer {
private Button cancelListButton;
private IWorkerCRUDController workerCRUDControllerRedirector;
private WorkerCRUDLinks workerCRUD;
public void showList(Event event) {
loadDataToList();
@ -63,7 +63,7 @@ public class CriterionWorkersController extends GenericForwardComposer {
}
public void goToEditPage(Resource resource) {
workerCRUDControllerRedirector.goToEditForm((Worker) resource);
workerCRUD.goToEditForm((Worker) resource);
}
@Override

View file

@ -1,9 +1,6 @@
package org.navalplanner.web.resources.worker;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.hibernate.validator.InvalidValue;
import org.navalplanner.business.common.exceptions.ValidationException;
@ -11,7 +8,6 @@ import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.IMessagesForUser;
import org.navalplanner.web.common.IRedirectorRegistry;
import org.navalplanner.web.common.Level;
import org.navalplanner.web.common.MatrixParameters;
import org.navalplanner.web.common.MessagesForUser;
import org.navalplanner.web.common.OnlyOneVisible;
import org.navalplanner.web.common.Redirector;
@ -25,7 +21,7 @@ import org.zkoss.zul.api.Window;
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class WorkerCRUDController extends GenericForwardComposer implements
IWorkerCRUDController {
WorkerCRUDLinks {
private Window createWindow;
@ -128,18 +124,9 @@ public class WorkerCRUDController extends GenericForwardComposer implements
if (messagesContainer == null)
throw new RuntimeException("messagesContainer is needed");
messages = new MessagesForUser(messagesContainer);
Map<String, String> matrixParameters = MatrixParameters
.extract((HttpServletRequest) execution.getNativeRequest());
Redirector redirector = redirectorRegistry
.getRedirectorFor(IWorkerCRUDController.class);
Redirector<WorkerCRUDLinks> redirector = redirectorRegistry
.getRedirectorFor(WorkerCRUDLinks.class);
redirector.applyTo(this);
// if (matrixParameters.containsKey("create")) {
// goToCreateForm();
// } else if (matrixParameters.containsKey("edit")) {
// goToEditForm(workerModel.findResource(Long
// .parseLong(matrixParameters.get("edit"))));
// }
}
private LocalizationsController createLocalizationsController(

View file

@ -1,40 +0,0 @@
package org.navalplanner.web.resources.worker;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.IRedirectorRegistry;
import org.navalplanner.web.common.Redirecter;
import org.navalplanner.web.common.Redirector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Redirects to the page composed by {@link WorkerCRUDController} <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
@Component
@Redirecter
public class WorkerCRUDControllerRedirector implements IWorkerCRUDController {
private Redirector<?> redirector;
@Autowired
public WorkerCRUDControllerRedirector(IRedirectorRegistry registry) {
redirector = registry.getRedirectorFor(IWorkerCRUDController.class);
}
@Override
public void goToCreateForm() {
redirector.doRedirect();
}
@Override
public void goToEditForm(Worker worker) {
redirector.doRedirect(worker);
}
@Override
public void goToWorkRelationshipsForm(Worker worker) {
redirector.doRedirect(worker);
}
}

View file

@ -1,23 +1,23 @@
package org.navalplanner.web.resources.worker;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.Linkable;
import org.navalplanner.web.common.Page;
import org.navalplanner.web.common.LinksDefiner;
import org.navalplanner.web.common.LinkToState;
/**
* Contract for {@link WorkerCRUDController}. <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
@Page("/resources/worker/worker.zul")
public interface IWorkerCRUDController {
@LinksDefiner(page = "/resources/worker/worker.zul", beanName = "workerCRUD")
public interface WorkerCRUDLinks {
@Linkable("edit")
@LinkToState("edit")
public abstract void goToEditForm(Worker worker);
@Linkable("workRelationships")
@LinkToState("workRelationships")
public abstract void goToWorkRelationshipsForm(Worker worker);
@Linkable("create")
@LinkToState("create")
public abstract void goToCreateForm();
}

View file

@ -13,6 +13,7 @@
required for "@Autowired")
-->
<context:annotation-config />
<bean class="org.navalplanner.web.common.RedirectorSynthetiser"></bean>
<context:component-scan base-package="org.navalplanner.web"/>
</beans>