All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.distributeme.generator.ServerGenerator Maven / Gradle / Ivy

package org.distributeme.generator;

import com.sun.mirror.apt.Filer;
import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.AnnotationValue;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.tools.apt.mirror.declaration.AnnotationValueImpl;
import net.anotheria.anoprise.metafactory.Extension;
import net.anotheria.anoprise.metafactory.FactoryNotFoundException;
import net.anotheria.anoprise.metafactory.MetaFactory;
import net.anotheria.anoprise.metafactory.ServiceFactory;
import net.anotheria.util.IdCodeGenerator;
import net.anotheria.util.PidTools;
import org.distributeme.annotation.CombinedService;
import org.distributeme.annotation.DistributeMe;
import org.distributeme.annotation.DummyFactory;
import org.distributeme.annotation.Route;
import org.distributeme.annotation.RouteMe;
import org.distributeme.annotation.ServerListener;
import org.distributeme.annotation.SupportService;
import org.distributeme.core.RMIRegistryUtil;
import org.distributeme.core.RegistryLocation;
import org.distributeme.core.RegistryUtil;
import org.distributeme.core.ServerShutdownHook;
import org.distributeme.core.ServiceDescriptor;
import org.distributeme.core.SystemPropertyNames;
import org.distributeme.core.Verbosity;
import org.distributeme.core.conventions.SystemProperties;
import org.distributeme.core.lifecycle.LifecycleComponentImpl;
import org.distributeme.core.listener.ListenerRegistry;
import org.distributeme.core.listener.ServerLifecycleListener;
import org.distributeme.core.listener.ServerLifecycleListenerShutdownHook;
import org.distributeme.core.routing.RegistrationNameProvider;
import org.distributeme.core.routing.RoutingAware;
import org.distributeme.core.util.LocalServiceDescriptorStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ExportException;
import java.rmi.server.UnicastRemoteObject;
import java.security.Permission;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Generator for RMI based server programm. 
 * @author lrosenberg
 */
public class ServerGenerator extends AbstractGenerator implements Generator{
	
	private static final String[] SUPPORT_SERVICES_ONLY = {
		"org.distributeme.support.lifecycle.generated.LifecycleSupportServer",
		"org.distributeme.support.eventservice.generated.EventServiceRMIBridgeServer",
	};

	private static final String[] SUPPORT_SERVICES_WITH_AGENTS = {
		"org.distributeme.support.lifecycle.generated.LifecycleSupportServer",
		"org.distributeme.support.eventservice.generated.EventServiceRMIBridgeServer",
		"org.distributeme.agents.transporter.generated.TransporterServer",
	};

	@Override
	public void generate(TypeDeclaration type, Filer filer, Map options) throws IOException{
		PrintWriter writer = filer.createSourceFile(getPackageName(type)+"."+getServerName(type));
		setWriter(writer);
		
		//meta servers rely on other skeletons and just starts them.
		boolean combinedServer = type.getAnnotation(CombinedService.class)!=null;
		
		writePackage(type);
		writeAnalyzerComments(type);
		emptyline();
		writeImport(Logger.class);
		writeImport(LoggerFactory.class);
		writeImport(Marker.class);
		writeImport(MarkerFactory.class);
		writeImport(UnicastRemoteObject.class);
		writeImport(Permission.class);
		writeImport(LocateRegistry.class);
		writeImport(Registry.class);
		writeImport(RegistryUtil.class);
		writeImport(RegistryLocation.class);
		writeImport(ExportException.class);
		writeImport(ServiceDescriptor.class);
		writeImport(LocalServiceDescriptorStore.class);
		writeImport("org.distributeme.core.ServiceDescriptor.Protocol");
		writeImport(MetaFactory.class);
		writeImport(FactoryNotFoundException.class);
		writeImport(Extension.class);
		writeImport(PidTools.class);
		writeImport(IdCodeGenerator.class);
		writeImport(RMIRegistryUtil.class);
		writeImport(RemoteException.class);
		writeImport(ServiceFactory.class);
		writeImport(LifecycleComponentImpl.class);
		writeImport(Verbosity.class);
		writeImport(SystemPropertyNames.class);
		writeImport(ServerShutdownHook.class);
		writeImport(SystemProperties.class);
		writeImport(List.class);
		writeImport(RoutingAware.class);
		emptyline();
		
		DistributeMe annotation = type.getAnnotation(DistributeMe.class);
		boolean supportService = type.getAnnotation(SupportService.class)!=null;
		String [] SUPPORT_SERVICES = annotation.agentsSupport() ? SUPPORT_SERVICES_WITH_AGENTS : SUPPORT_SERVICES_ONLY;
		if (!supportService){
			for (String sService : SUPPORT_SERVICES){
				writeImport(sService);
			}
		}

		writeString("public class "+getServerName(type)+"{");
		increaseIdent();
		emptyline();
		writeStatement("private static Logger log");
		writeStatement("private static Marker FATAL = MarkerFactory.getMarker(\"FATAL\")");
		emptyline();

		
		//checking for support service.
		if (!supportService){
			//only support service gets a main method
			writeString("public static void main(String a[]) throws Exception{");
			increaseIdent();

			writeString("if (System.getSecurityManager()==null)");
			increaseIdent();
			writeCommentLine("We allow all operations.");
			writeString("System.setSecurityManager(new SecurityManager(){");
			writeIncreasedString("public void checkPermission(Permission perm) { }");
			writeStatement("})");
			decreaseIdent();

			writeString("try {");
			increaseIdent();
			writeStatement("init()");
			writeCommentLine("Log current server PID (Process Id)");
			writeStatement("PidTools.logPid()");
			writeCommentLine("force verbosity to configure itself");
			writeStatement("Verbosity.logServerSideExceptions()");
			writeStatement("createSupportServicesAndRegisterLocally()");
			if(!combinedServer){
				writeStatement("createServiceAndRegisterLocally()");
			}
			if(combinedServer){
				writeStatement("createCombinedServicesAndRegisterLocally()");
			}
			writeStatement("startService()");
			writeStatement("notifyListenersAboutStart()");
			//writeStatement("this(new "+type.getAnnotation(DistributeMe.class).implName()+"())");
			decreaseIdent();
			writeString("} catch (Throwable e) {");
			
			increaseIdent();
			writeStatement("log.error(FATAL, \"Unhandled exception caught\", e)");
			writeStatement("System.err.println(e.getMessage())");
			writeStatement("System.exit(-4)");
			decreaseIdent();
			writeString("}");
			closeBlock("main");
			emptyline();
		}
		
		if (supportService){
			writeCommentLine("Support service have no main method");
		}
		
		List mirrors = findMirrors(type, ServerListener.class);
		writeStatement("private static final List<"+ServerLifecycleListener.class.getName()+"> serverListeners = new "+ArrayList.class.getName()+"<"+ServerLifecycleListener.class.getName()+">("+(mirrors==null ? 0:mirrors.size())+")");
		writeString("private static void notifyListenersAboutStart(){");
		increaseIdent();
		if (mirrors!=null && mirrors.size()>0){
			writeCommentLine("compiled listeners");
			for (AnnotationMirror mirror : mirrors){
				writeString("try{");
				increaseIdent();
				writeStatement(ServerLifecycleListener.class.getName()+" listener = new "+findMethodValue(mirror, "listenerClass").getValue()+"()");
				writeStatement("listener.afterStart()");
				writeCommentLine("Only add successful listeners");
				writeStatement("serverListeners.add(listener)");
				decreaseIdent();
				writeString("}catch(Exception e){");
				writeIncreasedStatement("log.error("+quote("Couldn't initialize listener "+findMethodValue(mirror, "listenerClass").getValue())+", e)");
				writeString("}");
			}
			//writeStatement("private static final "+ServerListener.class.getName()+" serverListeners = new "+ArrayList.class.getName()+"<"+ServerListener.class.getName()+">("+mirrors.size()+")");
			emptyline();
		}
		
		writeCommentLine("configured listeners");
		writeStatement("List<"+ServerLifecycleListener.class.getName()+"> configuredListeners = "+ListenerRegistry.class.getName()+".getInstance().getServerLifecycleListeners()");
		writeString("if (configuredListeners!=null && configuredListeners.size()>0){");
		increaseIdent();
		writeString("for ("+ServerLifecycleListener.class.getName()+" listener : configuredListeners){");
		increaseIdent();
		writeString("try{");
		increaseIdent();
		writeStatement("listener.afterStart()");
		decreaseIdent();
		writeString("}catch(Exception e){");
		writeIncreasedStatement("log.error("+quote("Couldn't call afterStart on  listener ")+" + listener, e)");
		writeString("}");
		closeBlock("for");
		closeBlock("if");
		
		
		
		
		closeBlock("notifyListenersAboutStart");
		emptyline();
		
		
		writeString("public static void init() throws Exception{");
		increaseIdent();
		writeStatement("log = LoggerFactory.getLogger("+getServerName(type)+".class)");
		writeCommentLine("// CUSTOM CODE STARTED");
		String[] initCode = annotation.initcode();
		for (String s : initCode){
			writeString(s);
		}
		writeCommentLine("// CUSTOM CODE ENDED");
		
		closeBlock("init");
		emptyline();
		
		if (!combinedServer){
			//START METHOD createServiceAndRegisterLocally
			writeCommentLine("Have to keep local reference to the rmiServant and skeleton to prevent gc removal");
			writeStatement("private static "+getRemoteInterfaceName(type)+" skeleton = null"); 
			writeStatement("private static "+getRemoteInterfaceName(type)+" rmiServant = null");
			writeStatement("private static String serviceId = null");
			emptyline();
			writeString("public static void createServiceAndRegisterLocally() throws Exception{");
			increaseIdent();
			writeCommentLine("creating impl");
			//old , direct instantiation writeStatement(type.getQualifiedName()+" impl = new "+type.getAnnotation(DistributeMe.class).factoryClassName()+"().create()");
			AnnotationMirror annotationMirror = findMirror(type, DistributeMe.class);
			if (annotationMirror==null)
				throw new AssertionError("AnnotationMirror is null, which actually can't happen, since the annotation was previously found: "+annotation);
			AnnotationValue factoryClazzValue = findMethodValue(annotationMirror, "factoryClazz");
				
			String factoryClassName = getDefaultImplFactoryName(type);
			String implClassName = type.getQualifiedName()+"Impl";
			
			if (factoryClazzValue!=null && !(factoryClazzValue.getValue().equals(DummyFactory.class.getName()+".class"))){
				writeCommentLine("Registering factory");
				writeStatement("MetaFactory.addFactoryClass("+type+".class, Extension."+annotation.extension()+", "+factoryClazzValue.getValue()+".class)");
			}else{
				writeCommentLine("No factory specified");
				if (initCode.length>0){
					writeCommentLine("init code not empty, assuming it contains factory declaration");
				}else{
					//try autoresolve
					writeString("try{");
					increaseIdent();
					writeStatement("Class> factoryClazz = (Class>)Class.forName("+quote(factoryClassName)+")");
					writeStatement("MetaFactory.addFactoryClass("+type+".class, Extension."+annotation.extension()+", factoryClazz)");
					decreaseIdent();
					writeString("}catch(ClassNotFoundException factoryNotFound){");
					increaseIdent();
					writeString("try{");
					increaseIdent();
					writeCommentLine("Even more convenient - try to instantiate the implementation directly");
					writeStatement("Class implClazz = (Class)Class.forName("+quote(implClassName)+")");
					writeStatement("MetaFactory.createOnTheFlyFactory("+type+".class, Extension."+annotation.extension()+", implClazz.newInstance())");
					decreaseIdent();
					writeString("}catch(ClassNotFoundException implNotFound){");
					increaseIdent();
					writeStatement("log.info("+quote("Giving up trying to find an impl instance, tried "+factoryClassName+" and "+implClassName+", expect start to fail since init code were empty too and no factory has been supplied explicitely")+")");
					closeBlock("inner catch");
					closeBlock("outer catch");
				}
			}
			writeStatement(type.getQualifiedName()+" impl = null");
			writeString("try{");
			increaseIdent();
			writeStatement("impl = MetaFactory.get(" + type.getQualifiedName() + ".class, Extension." + annotation.extension() + ")");
			decreaseIdent();
			writeString("}catch (FactoryNotFoundException factoryNotFound){");
			writeIncreasedStatement("throw new AssertionError(" + quote("Un- or mis-configured, can't instantiate service instance for " + type.getQualifiedName() + " tried initcode, submitted factory, autoguessed factory (" + factoryClassName + ") and impl class (" + implClassName + ")") + ")");
			writeString("}");
			
			
			writeStatement("skeleton = new " + getSkeletonName(type) + "(impl)");
			writeStatement("rmiServant = (" + getRemoteInterfaceName(type) + ") UnicastRemoteObject.exportObject(skeleton, SystemProperties.SERVICE_BINDING_PORT.getAsInt())");
			writeStatement("serviceId = "+getConstantsName(type)+".getServiceId()");
			writeCommentLine("Save original serviceId for later RoutingAware call");
			writeStatement("String definedServiceId = serviceId");
			emptyline();
			
			//determine whether we have a custom registration name.
			if (type.getAnnotation(RouteMe.class)!=null){
				emptyline();
				writeCommentLine("Customizing registration name");
				AnnotationMirror routeMe = findMirror(type, RouteMe.class);
				AnnotationValue registrationNameProviderClazzValue = findMethodValue(routeMe, "providerClass");
				AnnotationValue registrationNameProviderParameterValue = findMethodValue(routeMe, "providerParameter");
				writeStatement(RegistrationNameProvider.class.getName()+" nameProvider = new "+registrationNameProviderClazzValue.getValue()+"()");
				writeStatement("nameProvider.customize("+quote(registrationNameProviderParameterValue.getValue())+")");
				writeStatement("serviceId = nameProvider.getRegistrationName(serviceId)");
				emptyline();
			}
			
			//determine whether we have a registration name provider
			writeStatement("String regNameProviderClass = System.getProperty(SystemPropertyNames.REGISTRATION_NAME_PROVIDER)");
			writeString("if (regNameProviderClass!=null){");
			increaseIdent();
			writeStatement(RegistrationNameProvider.class.getName()+" suppliedNameProvider = ("+RegistrationNameProvider.class.getName()+")Class.forName(regNameProviderClass).newInstance()");
			writeStatement("serviceId = suppliedNameProvider.getRegistrationName(serviceId)");
			closeBlock("if (regNameProviderClass!=null)");
			
			emptyline();
			writeStatement("log.info("+quote("Getting local registry")+")");
			writeStatement("Registry registry = null");
			openTry();
			writeStatement("registry = RMIRegistryUtil.findOrCreateRegistry()");
			decreaseIdent();
			writeString("}catch(RemoteException e){");
			increaseIdent();
			writeStatement("log.error(FATAL, "+quote("Couldn't obtain free port for a local rmi registry")+", e)");
			writeStatement("System.err.println("+quote("Couldn't obtain a free port for local rmi registry")+")");
			writeStatement("System.exit(-1)"); 
			closeBlock();
					
			emptyline();
			writeStatement("log.info(" + quote("Registering ") + "+serviceId+" + quote(" locally.") + ")");
		
			emptyline();
			openTry();
			writeStatement("registry.rebind(serviceId, rmiServant)");
			decreaseIdent();
			writeStatement("}catch(Exception e){");
			increaseIdent();
			writeStatement("log.error(FATAL, "+quote("Couldn't rebind myself at the local registry")+", e)");
			writeStatement("System.err.println("+quote("Couldn't rebind myself at the local registry")+")");
			writeStatement("e.printStackTrace()");
			writeStatement("System.exit(-2)"); 
			closeBlock("local registry bind.");
			emptyline();

			//ROUTING AWARE-NESS.
			AnnotationMirror annotationMirrorRoute = findMirror(type, Route.class);
			if (annotationMirror!=null){
				AnnotationValue routerParameter = findMethodValue(annotationMirror, "routerParameter");
				AnnotationValue configurationName = findMethodValue(annotationMirror, "configurationName");


				//after registration, if service is RoutingAware we should notify it about its name.
				//of course it only makes sense if the service had Route annotation.
				writeString("if (impl instanceof RoutingAware){");
				System.out.println("RP: " + routerParameter);
				System.out.println("CN: "+configurationName);
				increaseIdent();
				writeStatement("((RoutingAware)impl).notifyServiceId(definedServiceId, serviceId, "
						+ (routerParameter == null ? "null" : quote(routerParameter.getValue()))
						+ ", "
						+ (configurationName == null ? "null" : quote(configurationName.getValue()))
						+ ") ");
				closeBlock("/if impl RoutingAware");
			}


			//finally locally register service
			if (!supportService){
				writeStatement("LifecycleComponentImpl.INSTANCE.registerPublicService(serviceId, skeleton)");
			}
			closeBlock();
			emptyline();
		}
		
		
		//combined server have no own descriptors.
		if (!combinedServer){
			writeString("public static ServiceDescriptor createDescriptor(String instanceId) throws Exception{");
			String descriptorCall = "RegistryUtil.createLocalServiceDescription("+
			"Protocol.RMI, "+
			" serviceId,"+
			" instanceId,"+
			" RMIRegistryUtil.getRmiRegistryPort())";
			increaseIdent();
			writeStatement("return "+descriptorCall);
			closeBlock();
		}

		//METHOD startService
		writeString("public static void startService() throws Exception{");
		increaseIdent();
//		writeStatement("String serviceId = "+getConstantsName(type)+".getServiceId()");
		writeStatement("String instanceId = IdCodeGenerator.generateCode(10)");

		
		if(!combinedServer){
		
			writeStatement("boolean registerCentrally = !SystemProperties.SKIP_CENTRAL_REGISTRY.getAsBoolean()");
			writeString("if (registerCentrally){");
			increaseIdent();
			writeStatement("ServiceDescriptor descriptor = createDescriptor(instanceId)");
			writeStatement("LocalServiceDescriptorStore.getInstance().addServiceDescriptor(descriptor)");
			
			emptyline();
			writeString("if (!RegistryUtil.bind(descriptor)){");
			increaseIdent();
			writeStatement("log.error(FATAL, "+quote("Couldn't bind myself to the central registry at ")+"+RegistryUtil.describeRegistry())");
			writeStatement("System.err.println("+quote("Couldn't bind myself at the central registry at ")+"+RegistryUtil.describeRegistry())");
			writeStatement("System.exit(-3)"); 
			closeBlock("central registry bind");
			writeStatement("Runtime.getRuntime().addShutdownHook(new ServerShutdownHook(descriptor))");
			emptyline();
			
			decreaseIdent();
			writeString("}else{");
			writeIncreasedStatement("System.out.println("+quote("skipping registration for ")+"+serviceId"+")");
			writeString("}");
			
			writeStatement("System.out.println("+quote("Server ")+"+serviceId+"+quote(" is up and ready.")+")");
		}

		if(combinedServer){
			List serviceNames = getCombinedServicesNames(type);
			for (String service : serviceNames){
				String descriptorVariableName = getServerName(service).toLowerCase()+"Descriptor";
				String serviceServerClassName = getFullyQualifiedServerName(service);
				writeStatement("ServiceDescriptor "+descriptorVariableName+" = "+serviceServerClassName+".createDescriptor(instanceId)");
				writeStatement("LocalServiceDescriptorStore.getInstance().addServiceDescriptor("+descriptorVariableName+")");

				emptyline();
				writeString("if (!RegistryUtil.bind("+descriptorVariableName+")){");
				increaseIdent();
				writeStatement("log.error(FATAL, "+quote("Couldn't bind ")+"+"+descriptorVariableName+"+"+quote(" to the central registry at ")+"+RegistryUtil.describeRegistry())");
				writeStatement("System.err.println("+quote("Couldn't bind ")+"+"+descriptorVariableName+"+"+quote(" at the central registry at ")+"+RegistryUtil.describeRegistry())");
				writeStatement("System.exit(-3)"); 
				closeBlock("central registry bind");
				writeStatement("Runtime.getRuntime().addShutdownHook(new ServerShutdownHook("+descriptorVariableName+"))");
				emptyline();
				writeStatement("System.out.println("+quote("Server ")+"+"+descriptorVariableName+".getServiceId()+"+quote(" is up and ready.")+")");
			}
		}

		//note, this shutdown hook will be activated AFTER de-registration hook.
		writeStatement("Runtime.getRuntime().addShutdownHook(new "+ServerLifecycleListenerShutdownHook.class.getName()+"(serverListeners))");
		closeBlock("startService");
		
		
		if (!supportService){
			emptyline();
			//METHOD createSupportServicesAndRegisterLocally
			writeString("public static void createSupportServicesAndRegisterLocally() throws Exception{");
			increaseIdent();
			
			for (String s : SUPPORT_SERVICES){
				writeStatement(s+".init()");
				writeStatement(s+".createServiceAndRegisterLocally()");
			}
			closeBlock("createSupportServicesAndRegisterLocally");
			emptyline();
		}
		
		if (combinedServer){
			emptyline();
			writeString("public static void createCombinedServicesAndRegisterLocally() throws Exception{");
			increaseIdent();
			List targetServicesNames = getCombinedServicesNames(type);
			for (String service : targetServicesNames){
				writeStatement(getFullyQualifiedServerName(service)+".init()");
				writeStatement(getFullyQualifiedServerName(service)+".createServiceAndRegisterLocally()");
			}
//			for (String s : SUPPORT_SERVICES){
//				writeStatement(s+".init()");
//				writeStatement(s+".createServiceAndRegisterLocally()");
//			}
			closeBlock("createCombinedServicesAndRegisterLocally");
			emptyline();
		}
			
		closeBlock();
		
		
		writer.flush();
		writer.close();
	}
	
	private List getCombinedServicesNames(TypeDeclaration type){
		AnnotationMirror ann = findMirror(type, CombinedService.class);
		AnnotationValue val = findMethodValue(ann, "services");
		@SuppressWarnings("unchecked")ArrayList values = (ArrayList)val.getValue();
		ArrayList ret = new ArrayList();
		for (AnnotationValueImpl o : values){
			ret.add(o.getValue().toString());
		}
		return ret;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy