/**
* Mule Development Kit
* Copyright 2010-2011 (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
*
* 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.mule.devkit.generation.adapter;
import org.mule.api.DefaultMuleException;
import org.mule.api.MuleException;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.ExpressionLanguage;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.lifecycle.Start;
import org.mule.api.annotations.lifecycle.Stop;
import org.mule.api.lifecycle.Disposable;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.lifecycle.Startable;
import org.mule.api.lifecycle.Stoppable;
import org.mule.config.MuleManifest;
import org.mule.devkit.generation.AbstractModuleGenerator;
import org.mule.devkit.generation.DevKitTypeElement;
import org.mule.devkit.generation.NamingContants;
import org.mule.devkit.model.code.Block;
import org.mule.devkit.model.code.CatchBlock;
import org.mule.devkit.model.code.Conditional;
import org.mule.devkit.model.code.DefinedClass;
import org.mule.devkit.model.code.ExpressionFactory;
import org.mule.devkit.model.code.ForLoop;
import org.mule.devkit.model.code.Invocation;
import org.mule.devkit.model.code.Method;
import org.mule.devkit.model.code.Modifier;
import org.mule.devkit.model.code.Op;
import org.mule.devkit.model.code.TryStatement;
import org.mule.devkit.model.code.TypeReference;
import org.mule.devkit.model.code.Variable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.util.List;
public class LifecycleAdapterGenerator extends AbstractModuleGenerator {
@Override
protected boolean shouldGenerate(DevKitTypeElement typeElement) {
return typeElement.hasAnnotation(Module.class) || typeElement.hasAnnotation(Connector.class) || typeElement.hasAnnotation(ExpressionLanguage.class);
}
@Override
protected void doGenerate(DevKitTypeElement typeElement) {
DefinedClass lifecycleAdapter = getLifecycleAdapterClass(typeElement);
lifecycleAdapter.javadoc().add("A <code>" + lifecycleAdapter.name() + "</code> is a wrapper around ");
lifecycleAdapter.javadoc().add(ref(typeElement.asType()));
lifecycleAdapter.javadoc().add(" that adds lifecycle methods to the pojo.");
ExecutableElement startElement = getStartElement(typeElement);
lifecycleAdapter._implements(Startable.class);
Method start = generateLifecycleInvocation(lifecycleAdapter, typeElement, startElement, "start", DefaultMuleException.class, false);
start._throws(ref(MuleException.class));
ExecutableElement stopElement = getStopElement(typeElement);
lifecycleAdapter._implements(Stoppable.class);
Method stop = generateLifecycleInvocation(lifecycleAdapter, typeElement, stopElement, "stop", DefaultMuleException.class, false);
stop._throws(ref(MuleException.class));
ExecutableElement postConstructElement = getPostConstructElement(typeElement);
lifecycleAdapter._implements(Initialisable.class);
generateLifecycleInvocation(lifecycleAdapter, typeElement, postConstructElement, "initialise", InitialisationException.class, true);
ExecutableElement preDestroyElement = getPreDestroyElement(typeElement);
lifecycleAdapter._implements(Disposable.class);
generateLifecycleInvocation(lifecycleAdapter, typeElement, preDestroyElement, "dispose", null, false);
}
private DefinedClass getLifecycleAdapterClass(TypeElement typeElement) {
String lifecycleAdapterName = context.getNameUtils().generateClassName(typeElement, NamingContants.ADAPTERS_NAMESPACE, NamingContants.LIFECYCLE_ADAPTER_CLASS_NAME_SUFFIX);
org.mule.devkit.model.code.Package pkg = context.getCodeModel()._package(context.getNameUtils().getPackageName(lifecycleAdapterName));
TypeReference previous = context.getClassForRole(context.getNameUtils().generateModuleObjectRoleKey(typeElement));
if (previous == null) {
previous = (TypeReference) ref(typeElement.asType());
}
int modifiers = Modifier.PUBLIC;
if( typeElement.getModifiers().contains(javax.lang.model.element.Modifier.ABSTRACT) ) {
modifiers |= Modifier.ABSTRACT;
}
DefinedClass clazz = pkg._class(modifiers, context.getNameUtils().getClassName(lifecycleAdapterName), previous);
context.setClassRole(context.getNameUtils().generateModuleObjectRoleKey(typeElement), clazz);
return clazz;
}
private Method generateLifecycleInvocation(DefinedClass lifecycleWrapper, DevKitTypeElement typeElement, ExecutableElement superExecutableElement, String name, Class<?> catchException, boolean addThis) {
Method lifecycleMethod = lifecycleWrapper.method(Modifier.PUBLIC, context.getCodeModel().VOID, name);
if (name.equals("initialise")) {
Variable log = lifecycleMethod.body().decl(ref(Logger.class), "log", ref(LoggerFactory.class).staticInvoke("getLogger").arg(lifecycleWrapper.dotclass()));
Variable runtimeVersion = lifecycleMethod.body().decl(ref(String.class), "runtimeVersion", ref(MuleManifest.class).staticInvoke("getProductVersion"));
Conditional ifUnkownVersion = lifecycleMethod.body()._if(runtimeVersion.invoke("equals").arg("Unknown"));
ifUnkownVersion._then().add(log.invoke("warn").arg(ExpressionFactory.lit("Unknown Mule runtime version. This module may not work properly!")));
Block ifKnownVersion = ifUnkownVersion._else();
Variable expectedMinVersion = ifKnownVersion.decl(ref(String[].class), "expectedMinVersion", ExpressionFactory.lit(typeElement.minMuleVersion()).invoke("split").arg("\\."));
Block ifKnownVersionContainsDash = ifKnownVersion._if(ExpressionFactory.invoke(runtimeVersion, "contains").arg("-"))._then();
ifKnownVersionContainsDash.assign(runtimeVersion, runtimeVersion.invoke("split").arg("-").component(ExpressionFactory.lit(0)));
Variable currentRuntimeVersion = ifKnownVersion.decl(ref(String[].class), "currentRuntimeVersion", runtimeVersion.invoke("split").arg("\\."));
ForLoop forEachVersionComponent = ifKnownVersion._for();
Variable i = forEachVersionComponent.init(context.getCodeModel().INT, "i", ExpressionFactory.lit(0));
forEachVersionComponent.test(Op.lt(i, expectedMinVersion.ref("length")));
forEachVersionComponent.update(Op.incr(i));
TryStatement tryToParseMuleVersion = forEachVersionComponent.body()._try();
tryToParseMuleVersion.body()._if(Op.lt(
ref(Integer.class).staticInvoke("parseInt").arg(currentRuntimeVersion.component(i)),
ref(Integer.class).staticInvoke("parseInt").arg(expectedMinVersion.component(i))))._then()
._throw(ExpressionFactory._new(ref(RuntimeException.class)).arg("This module is only valid for Mule " + typeElement.minMuleVersion()));
CatchBlock catchBlock = tryToParseMuleVersion._catch(ref(NumberFormatException.class));
catchBlock.param("nfe");
catchBlock.body().invoke(log, "warn").arg("Error parsing Mule version, cannot validate current Mule version");
}
if (catchException != null &&
superExecutableElement != null &&
superExecutableElement.getThrownTypes() != null &&
superExecutableElement.getThrownTypes().size() > 0) {
lifecycleMethod._throws(ref(catchException));
}
if (superExecutableElement != null) {
Invocation startInvocation = ExpressionFactory._super().invoke(superExecutableElement.getSimpleName().toString());
if (superExecutableElement.getThrownTypes().size() > 0) {
TryStatement tryBlock = lifecycleMethod.body()._try();
tryBlock.body().add(startInvocation);
int i = 0;
for (TypeMirror exception : superExecutableElement.getThrownTypes()) {
CatchBlock catchBlock = tryBlock._catch(ref(exception).boxify());
Variable catchedException = catchBlock.param("e" + i);
Invocation newMuleException = ExpressionFactory._new(ref(catchException));
newMuleException.arg(catchedException);
if (addThis) {
newMuleException.arg(ExpressionFactory._this());
}
catchBlock.body().add(newMuleException);
i++;
}
} else {
lifecycleMethod.body().add(startInvocation);
}
}
return lifecycleMethod;
}
private ExecutableElement getStartElement(DevKitTypeElement typeElement) {
List<ExecutableElement> startMethods = typeElement.getMethodsAnnotatedWith(Start.class);
return !startMethods.isEmpty() ? startMethods.get(0) : null;
}
private ExecutableElement getStopElement(DevKitTypeElement typeElement) {
List<ExecutableElement> stopMethods = typeElement.getMethodsAnnotatedWith(Stop.class);
return !stopMethods.isEmpty() ? stopMethods.get(0) : null;
}
private ExecutableElement getPostConstructElement(DevKitTypeElement typeElement) {
List<ExecutableElement> postConstructMethods = typeElement.getMethodsAnnotatedWith(PostConstruct.class);
return !postConstructMethods.isEmpty() ? postConstructMethods.get(0) : null;
}
private ExecutableElement getPreDestroyElement(DevKitTypeElement typeElement) {
List<ExecutableElement> preDestroyMethods = typeElement.getMethodsAnnotatedWith(PreDestroy.class);
return !preDestroyMethods.isEmpty() ? preDestroyMethods.get(0) : null;
}
}