/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.ejb.gen;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.ejb.TransactionAttribute;
import static javax.ejb.TransactionAttributeType.REQUIRED;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import com.caucho.config.gen.AspectBeanFactory;
import com.caucho.config.gen.AspectGenerator;
import com.caucho.config.gen.BeanGenerator;
import com.caucho.config.gen.LifecycleAspectBeanFactory;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.reflect.AnnotatedTypeUtil;
import com.caucho.inject.Module;
import com.caucho.java.JavaWriter;
/**
* Generates the skeleton for a message bean.
*/
@Module
public class MessageGenerator<X> extends BeanGenerator<X> {
private AspectBeanFactory<X> _aspectBeanFactory;
private ArrayList<AspectGenerator<X>> _businessMethods
= new ArrayList<AspectGenerator<X>>();
private ArrayList<AnnotatedMethod<? super X>> _annotatedMethods
= new ArrayList<AnnotatedMethod<? super X>>();
private final AspectBeanFactory<X> _lifecycleAspectFactory;
public MessageGenerator(String ejbName, AnnotatedType<X> ejbClass)
{
super(toFullClassName(ejbName, ejbClass.getJavaClass().getName()), ejbClass);
InjectManager manager = InjectManager.create();
_aspectBeanFactory = new MessageAspectBeanFactory<X>(manager, getBeanType());
_lifecycleAspectFactory = new LifecycleAspectBeanFactory<X>(_aspectBeanFactory, manager, getBeanType());
}
protected AspectBeanFactory<X> getLifecycleAspectFactory()
{
return _lifecycleAspectFactory;
}
private static String toFullClassName(String ejbName, String className)
{
StringBuilder sb = new StringBuilder();
/*
sb.append("_ejb.");
if (! Character.isJavaIdentifierStart(ejbName.charAt(0)))
sb.append('_');
for (int i = 0; i < ejbName.length(); i++) {
char ch = ejbName.charAt(i);
if (ch == '/')
sb.append('.');
else if (Character.isJavaIdentifierPart(ch))
sb.append(ch);
else
sb.append('_');
}
sb.append(".");
sb.append(className);
*/
sb.append(className);
sb.append("__MessageProxy");
return sb.toString();
}
public String getContextClassName()
{
return getClassName();
}
@Override
public String getViewClassName()
{
return getClassName();
}
@Override
protected AspectBeanFactory<X> getAspectBeanFactory()
{
return _aspectBeanFactory;
}
/**
* Returns the introspected methods
*/
@Override
public ArrayList<AspectGenerator<X>> getMethods()
{
return _businessMethods;
}
/**
* Introspects the bean.
*/
@Override
public void introspect()
{
super.introspect();
introspectType(getBeanType());
introspectImpl();
}
private void introspectType(AnnotatedType<? super X> type)
{
for (AnnotatedMethod<? super X> method : type.getMethods())
introspectMethod(method);
}
private void introspectMethod(AnnotatedMethod<? super X> method)
{
_annotatedMethods.add(method);
}
/**
* Introspects the APIs methods, producing a business method for
* each.
*/
private void introspectImpl()
{
for (AnnotatedMethod<? super X> method : _annotatedMethods) {
introspectMethodImpl(method);
}
}
private void introspectMethodImpl(AnnotatedMethod<? super X> apiMethod)
{
Method javaMethod = apiMethod.getJavaMember();
if (javaMethod.getDeclaringClass().equals(Object.class))
return;
if (javaMethod.getDeclaringClass().getName().startsWith("javax.ejb."))
return;
/*
if (javaMethod.getName().startsWith("ejb")) {
throw new ConfigException(L.l("{0}: '{1}' must not start with 'ejb'. The EJB spec reserves all methods starting with ejb.",
javaMethod.getDeclaringClass(),
javaMethod.getName()));
}
*/
int modifiers = javaMethod.getModifiers();
if (! Modifier.isPublic(modifiers)) {
return;
}
if (Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers))
return;
if (apiMethod.isAnnotationPresent(PostConstruct.class))
addLifecycleMethod(apiMethod);
else
addBusinessMethod(apiMethod);
}
public void addBusinessMethod(AnnotatedMethod<? super X> method)
{
AspectGenerator<X> bizMethod
= _aspectBeanFactory.create(method);
if (bizMethod != null) {
_businessMethods.add(bizMethod);
}
}
protected void addLifecycleMethod(AnnotatedMethod<? super X> method)
{
if (getLifecycleAspectFactory() == null)
return;
AspectGenerator<X> initMethod = getLifecycleAspectFactory().create(method);
if (initMethod != null && ! _businessMethods.contains(initMethod)) {
_businessMethods.add(initMethod);
}
}
/**
* Generates the message session bean
*/
@Override
public void generate(JavaWriter out)
throws IOException
{
generateTopComment(out);
out.println();
out.println("package " + getPackageName() + ";");
out.println();
out.println("import com.caucho.config.*;");
out.println("import com.caucho.ejb.*;");
out.println("import com.caucho.ejb.message.*;");
out.println();
out.println("import java.util.*;");
out.println("import java.lang.reflect.*;");
out.println("import javax.ejb.*;");
out.println("import javax.transaction.*;");
out.println("import javax.transaction.xa.*;");
out.println("import javax.resource.spi.endpoint.*;");
out.println();
out.println("public class " + getClassName()
+ " extends " + getBeanType().getJavaClass().getName()
+ " implements MessageEndpoint, CauchoMessageEndpoint");
out.println("{");
out.pushDepth();
/*
// ejb/0931
out.println();
out.println("private static final com.caucho.ejb3.xa.XAManager _xa");
out.println(" = new com.caucho.ejb3.xa.XAManager();");
*/
out.println("private static HashSet<Method> _xaMethods = new HashSet<Method>();");
out.println();
out.println("private MessageManager _server;");
out.println("private XAResource _xaResource;");
out.println("private boolean _isXa;");
HashMap<String,Object> map = new HashMap<String,Object>();
map.put("caucho.ejb.xa", "true");
// view.generateContextPrologue(out);
generateBeanPrologue(out, map);
out.println();
//out.println("public " + getClassName() + "(MessageManager server)");
out.println("public " + getClassName() + "()");
out.println("{");
out.pushDepth();
// out.println("_server = server;");
/*
if (MessageDrivenBean.class.isAssignableFrom(getBeanType().getJavaClass())) {
out.println("setMessageDrivenContext(server.getMessageContext());");
}
*/
generateBeanConstructor(out);
out.popDepth();
out.println("}");
out.println();
out.println("static {");
out.pushDepth();
out.println("try {");
out.pushDepth();
// XXX: 4.0.7
for (AspectGenerator<X> bizMethod : getMethods()) {
AnnotatedMethod<?> method = bizMethod.getMethod();
TransactionAttribute xa;
xa = method.getAnnotation(TransactionAttribute.class);
if (xa == null)
xa = method.getDeclaringType().getAnnotation(TransactionAttribute.class);
if (xa != null && REQUIRED.equals(xa.value())) {
addXaMethods(out, method.getJavaMember());
}
}
out.popDepth();
out.println("} catch (Exception e) {");
out.println(" throw new RuntimeException(e);");
out.println("}");
out.popDepth();
out.println("}");
out.println();
out.println("public void __caucho_setXAResource(XAResource xaResource)");
out.println("{");
out.println(" _xaResource = xaResource;");
out.println("}");
out.println();
out.println("public void beforeDelivery(java.lang.reflect.Method method)");
out.println("{");
out.println(" if (_xaMethods.contains(method)) {");
out.println(" _isXa = (_xa.beginRequired() == null);");
out.println();
out.println(" if (_isXa && _xaResource != null)");
out.println(" _xa.enlist(_xaResource);");
out.println(" }");
out.println("}");
out.println("public void afterDelivery()");
out.println("{");
out.println(" if (_isXa) {");
out.println(" _isXa = false;");
out.println(" _xa.commit();");
out.println(" }");
out.println("}");
out.println();
out.println("public void release()");
out.println("{");
out.pushDepth();
if (getImplMethod("ejbRemove", new Class[0]) != null) {
out.println("ejbRemove();");
}
out.popDepth();
out.println("}");
/*
for (View view : getViews()) {
view.generateContextPrologue(out);
}
*/
generateView(out);
generateDependency(out);
out.popDepth();
out.println("}");
}
private void addXaMethods(JavaWriter out, Method method)
throws IOException
{
printXaMethod(out, method);
for (Class<?> api : method.getDeclaringClass().getInterfaces()) {
Method subMethod = AnnotatedTypeUtil.findMethod(api.getMethods(), method);
if (subMethod != null)
printXaMethod(out, subMethod);
}
}
private void printXaMethod(JavaWriter out, Method api)
throws IOException
{
out.print("_xaMethods.add(");
out.printClass(api.getDeclaringClass());
out.print(".class.getMethod(\"");
out.print(api.getName());
out.print("\", new Class[] { ");
for (Class<?> cl : api.getParameterTypes()) {
out.printClass(cl);
out.print(".class, ");
}
out.println("}));");
}
/**
* Generates the view code.
*/
private void generateView(JavaWriter out)
throws IOException
{
HashMap<String,Object> map = new HashMap<String,Object>();
map.put("caucho.ejb.xa", "done");
out.println();
out.println("private static final com.caucho.ejb.util.XAManager _xa");
out.println(" = new com.caucho.ejb.util.XAManager();");
/* ejb/0fbm
for (BusinessMethodGenerator bizMethod : _businessMethods) {
bizMethod.generatePrologueTop(out, map);
}
*/
for (AspectGenerator<X> bizMethod : _businessMethods) {
bizMethod.generate(out, map);
}
generatePostConstruct(out, map);
}
private AnnotatedMethod<? super X> getImplMethod(String name, Class<?> []param)
{
return AnnotatedTypeUtil.findMethod(getBeanType(), name, param);
}
}