ItEr10S08AdministracionGruposItEr09S09: Avoided the creation of a dumb controller for doing the redirections, this controller is synthesized instead.
This commit is contained in:
parent
9b3fa481d7
commit
6e80beb367
10 changed files with 227 additions and 121 deletions
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
Loading…
Add table
Reference in a new issue