/* * Copyright (C) 2013 Red Hat, Inc. and/or its affiliates. * * Licensed 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.jboss.errai.ioc.rebind.ioc.extension.builtin; import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs; import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf; import static org.jboss.errai.codegen.util.Stmt.castTo; import static org.jboss.errai.codegen.util.Stmt.declareFinalVariable; import static org.jboss.errai.codegen.util.Stmt.invokeStatic; import static org.jboss.errai.codegen.util.Stmt.nestedCall; import static org.jboss.errai.codegen.util.Stmt.newObject; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import org.jboss.errai.codegen.Parameter; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.builder.AnonymousClassStructureBuilder; import org.jboss.errai.codegen.builder.BlockBuilder; import org.jboss.errai.codegen.builder.ContextualStatementBuilder; import org.jboss.errai.codegen.builder.impl.ObjectBuilder; import org.jboss.errai.codegen.exception.GenerationException; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameter; import org.jboss.errai.codegen.util.GenUtil; import org.jboss.errai.codegen.util.Refs; import org.jboss.errai.ioc.client.api.CodeDecorator; import org.jboss.errai.ioc.client.api.UncaughtExceptionHandler; import org.jboss.errai.ioc.client.container.ErraiUncaughtExceptionHandler; import org.jboss.errai.ioc.client.container.Factory; import org.jboss.errai.ioc.rebind.ioc.extension.IOCDecoratorExtension; import org.jboss.errai.ioc.rebind.ioc.injector.api.Decorable; import org.jboss.errai.ioc.rebind.ioc.injector.api.FactoryController; import com.google.gwt.core.client.GWT; /** * Generates factory/instance initialization statements to register {@link UncaughtExceptionHandler} methods with the * {@link ErraiUncaughtExceptionHandler}. * * @author Christian Sadilek <csadilek@redhat.com> * @author Max Barkley <mbarkley@redhat.com> */ @CodeDecorator public class UncaughtExceptionDecorator extends IOCDecoratorExtension<UncaughtExceptionHandler> { public UncaughtExceptionDecorator(final Class<UncaughtExceptionHandler> decoratesWith) { super(decoratesWith); } @Override public void generateDecorator(final Decorable decorable, final FactoryController controller) { final MetaMethod method = validateExceptionHandlingMethod(decorable); final boolean enclosingTypeDependent = decorable.isEnclosingTypeDependent(); final String handlerVar = method.getName() + "Handler"; final String cleanupRunnableVar = method.getName() + "HandlerCleanup"; final Statement setCleanupRefStmt = controller.setReferenceStmt(cleanupRunnableVar, Refs.get(cleanupRunnableVar)); final Statement getCleanupRefStmt = controller.getReferenceStmt(cleanupRunnableVar, Runnable.class); final List<Statement> initStmts = new ArrayList<>(Arrays.asList( declareAndInitHandlerVar(decorable, controller, handlerVar, enclosingTypeDependent), declareFinalVariable(cleanupRunnableVar, Runnable.class, addHandler(handlerVar)))); if (enclosingTypeDependent) { initStmts.add(setCleanupRefStmt); } final List<Statement> destructionStmts = Collections.singletonList(nestedCall(getCleanupRefStmt).invoke("run")); if (enclosingTypeDependent) { controller.addInitializationStatements(initStmts); controller.addDestructionStatements(destructionStmts); } else { controller.addFactoryInitializationStatements(initStmts); } } private ContextualStatementBuilder addHandler(final String handlerVar) { return castTo(ErraiUncaughtExceptionHandler.class, invokeStatic(GWT.class, "getUncaughtExceptionHandler")) .invoke("addHandler", Refs.get(handlerVar)); } private MetaMethod validateExceptionHandlingMethod(final Decorable decorable) { // Ensure that method has exactly one parameter of type Throwable final MetaMethod method = decorable.getAsMethod(); final MetaParameter[] parms = method.getParameters(); if (!(parms.length == 1 && parms[0].getType().equals(MetaClassFactory.get(Throwable.class)))) { throw new GenerationException("Methods annotated with " + UncaughtExceptionHandler.class.getName() + " must have exactly one parameter of type " + Throwable.class.getName() + ". Invalid parameters in method: " + GenUtil.getMethodString(method) + " of type " + method.getDeclaringClass() + "."); } return method; } private Statement declareAndInitHandlerVar(final Decorable decorable, final FactoryController controller, final String name, final boolean enclosingTypeDependent) { final MetaClass throwableConsumerClass = parameterizedAs(Consumer.class, typeParametersOf(Throwable.class)); final BlockBuilder<AnonymousClassStructureBuilder> initBuilder = newObject(throwableConsumerClass) .extend().publicOverridesMethod("accept", Parameter.of(Throwable.class, "t")); if (!enclosingTypeDependent) { final MetaClass enclosingType = decorable.getEnclosingInjectable().getInjectedType(); initBuilder.append(declareFinalVariable("instance", enclosingType, castTo(enclosingType, invokeStatic(Factory.class, "maybeUnwrapProxy", controller.contextGetInstanceStmt())))); } final ObjectBuilder initStmt = initBuilder.append(decorable.call(Refs.get("t"))).finish().finish(); final Statement handlerStatement = declareFinalVariable(name, throwableConsumerClass, initStmt); return handlerStatement; } }