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

jadex.extension.rs.publish.GrizzlyRestServicePublishService Maven / Gradle / Ivy

There is a newer version: 4.0.267
Show newest version
package jadex.extension.rs.publish;

import java.io.Writer;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import jadex.commons.SAccess;
import org.glassfish.grizzly.http.server.CLStaticHttpHandler;
import org.glassfish.grizzly.http.server.ErrorPageGenerator;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.grizzly.http.server.StaticHttpHandler;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;

import jadex.bridge.IComponentIdentifier;
import jadex.bridge.ServiceCall;
import jadex.bridge.modelinfo.UnparsedExpression;
import jadex.bridge.service.IServiceIdentifier;
import jadex.bridge.service.PublishInfo;
import jadex.bridge.service.ServiceScope;
import jadex.bridge.service.annotation.Service;
import jadex.bridge.service.component.IRequiredServicesFeature;
import jadex.bridge.service.search.ServiceQuery;
import jadex.bridge.service.types.cms.IComponentDescription;
import jadex.bridge.service.types.library.ILibraryService;
import jadex.commons.Tuple2;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.javaparser.SJavaParser;

/**
 *  The default web service publish service.
 *  Publishes web services using the Grizzly web server.
 */
@Service
public class GrizzlyRestServicePublishService extends AbstractRestServicePublishService
{
	//-------- constants --------
	
	/** The servers per service id. */
	protected Map> sidservers;
	
	/** The servers per port. */
	protected Map> portservers;
	
	//-------- constructors --------
	
	/**
	 *  Create a new publish service.
	 */
	public GrizzlyRestServicePublishService()
	{
	}
	
	/**
	 *  Create a new publish service.
	 */
	public GrizzlyRestServicePublishService(IRestMethodGenerator generator)
	{
		super(generator);
	}
	
	//-------- methods --------
	
	/**
	 * 
	 */
	public void internalPublishService(URI uri, ResourceConfig rc, IServiceIdentifier sid, PublishInfo info)
	{
		try
		{
			Tuple2 servertuple = getHttpServer(uri, info);
			System.out.println("Adding http handler to server: "+uri.getPath());
			HttpHandler handler = ContainerFactory.createContainer(HttpHandler.class, rc);
//			ServerConfiguration sc = server.getServerConfiguration();
//			sc.addHttpHandler(handler, uri.getPath());
			servertuple.getFirstEntity().addSubhandler(null, uri.getPath(), handler);
			
			if(sidservers==null)
				sidservers = new HashMap>();
			sidservers.put(sid, new Tuple2(servertuple.getSecondEntity(), uri));
			
	//		Map handlers = server.getServerConfiguration().getHttpHandlers();
	//		for(HttpHandler hand: handlers.keySet())
	//		{
	//			Set set = SUtil.arrayToSet(handlers.get(hand));
	//			if(set.contains(uri.getPath()))
	//			{
	//				handler = hand;
	//			}
	//		}
	//		if(handler==null)
	//		{
	//			ret.setException(new RuntimeException("Publication error, failed to get http handler: "+uri.getPath()));
	//		}
		}
		catch(Exception e)
		{
			throw new RuntimeException(e);
		}
	}
	
	/**
	 *  Get or start an api to the http server.
	 */
	public Tuple2 getHttpServer(URI uri, PublishInfo info)
	{
		Tuple2 servertuple = null;
		
		try
		{
//			URI baseuri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), null, null, null);
			servertuple = portservers==null? null: portservers.get(uri.getPort());
			
			if(servertuple==null)
			{
				HttpServer server = startServer(uri, info, null);
				MainHttpHandler mainhandler = new MainHttpHandler();
				server.getServerConfiguration().addHttpHandler(mainhandler);
				servertuple = new Tuple2(mainhandler, server);
				portservers.put(uri.getPort(), servertuple);
			}
		}
		catch(RuntimeException e)
		{
			throw e;
		}
		catch(Exception e)
		{
			throw new RuntimeException(e);
		}
		
		return servertuple;
	}
	
	/**
	 *  Mirror an existing http server.
	 *  @param sourceserveruri The URI of the server being mirrored.
	 *  @param targetserveruri The URI of the mirror server.
	 *  @param info Publish infos for the mirror server.
	 */
	public IFuture mirrorHttpServer(URI sourceserveruri, URI targetserveruri, PublishInfo info)
	{
		Future ret = new Future();
		Tuple2 sourceservertuple = portservers==null? null: portservers.get(sourceserveruri.getPort());
		
		if (sourceservertuple != null)
		{
			try
			{
				String errorfallback = null;
				if ((sourceservertuple.getSecondEntity().getServerConfiguration().getDefaultErrorPageGenerator() instanceof RedirectErrorPageGenerator))
				{
					errorfallback = ((RedirectErrorPageGenerator) sourceservertuple.getSecondEntity().getServerConfiguration().getDefaultErrorPageGenerator()).getRedirectUrl();
				}
				HttpServer newserver = startServer(targetserveruri, info, errorfallback);
				newserver.getServerConfiguration().addHttpHandler(sourceservertuple.getFirstEntity());
				Tuple2 newservertuple = new Tuple2(sourceservertuple.getFirstEntity(), newserver);
				if (!(newserver.getServerConfiguration().getDefaultErrorPageGenerator() instanceof RedirectErrorPageGenerator))
				{
					ErrorPageGenerator epg = sourceservertuple.getSecondEntity().getServerConfiguration().getDefaultErrorPageGenerator();
					System.out.println(epg);
					if (epg != null)
					{
						newserver.getServerConfiguration().setDefaultErrorPageGenerator(epg);
					}
				}
				portservers.put(targetserveruri.getPort(), newservertuple);
				
			}
			catch (Exception e)
			{
				ret.setException(e);
			}
		}
		else
		{
			ret.setException(new RuntimeException("Server mirror source not found: " + sourceserveruri.toString()));
		}
		
		return ret;
	}
	
	/**
	 *  Explicitely terminated an existing http server.
	 *  @param uri URI of the server.
	 */
	public IFuture shutdownHttpServer(URI uri)
	{
		Tuple2 servertuple = getHttpServer(uri, null);
		for(Iterator> servers=portservers.values().iterator(); servers.hasNext(); )
    	{
    		if(servers.next().getSecondEntity().equals(servertuple.getSecondEntity()))
    		{
    			servers.remove();
    			break;
    		}
    	}
		System.out.println("Terminating server: "+uri.getPort());
    	servertuple.getSecondEntity().shutdownNow();
    	return IFuture.DONE;
	}
	
	/**
	 *  Test if a the web user is logged in.
	 *  @param callid The callid of the request.
	 *  @return True, if is logged in.
	 */
	public IFuture isLoggedIn(String callid)
	{
		// todo: implement me
		return new Future<>(Boolean.FALSE);
	}
	
//	/**
//	 * 
//	 */
//	public IFuture publishServet(URI uri, Object servlet)
//	{
//		HttpServer server = getHttpServer(uri, createConfig());
//		ServletConfigImpl conf = new ServletConfigImpl();
//		ServletHandler handler = new ServletHandler(conf);
//		handler.setContextPath(uri.getPath());
//        ServerConfiguration sc = server.getServerConfiguration();
//		sc.addHttpHandler(handler, uri.getPath());
//		
//		WebappContext ctx = new WebappContext(uri.getPath());
//		ServletRegistration reg = ctx.addServlet(SReflect.getInnerClassName(servlet.getClass()), servlet);
//		reg.addMapping(alias);
//		ctx.deploy(server);
//		
//		return IFuture.DONE;
//	}
	
	/**
	 *  Publish permanent redirect.
	 */
	public IFuture publishRedirect(final URI uri, final String html)
	{
		Tuple2 servertuple = getHttpServer(uri, null);
		
		if (servertuple.getFirstEntity().containsSubhandlerForExactUri(null, uri.getPath()))
		{
			return new Future(new IllegalArgumentException("Cannot redirect, URI already bound: " + uri.toString()));
		}
		
		HttpHandler redh	= new HttpHandler()
	    {
	    	public void service(Request request, Response response)
	    	{
	    		response.setStatus(HttpStatus.MOVED_PERMANENTLY_301);
	    		response.setHeader(Header.Location, html);
	    	}
	    };
	    
		servertuple.getFirstEntity().addSubhandler(null, uri.getPath(), redh);
		
		return IFuture.DONE;
	}
	
	/**
	 *  Publish an html page.x	
	 */
	public IFuture publishHMTLPage(String pid, String vhost, String html)
	{
		try
		{
			URI uri = new URI(pid);
			Tuple2 servertuple = getHttpServer(uri, null);
			
	//        ServerConfiguration sc = server.getServerConfiguration();
	//        Map	handlers	= sc.getHttpHandlers();
	//        HtmlHandler	htmlh	= null;
	//        for(Map.Entry entry: handlers.entrySet())
	//        {
	//        	if(entry.getKey() instanceof HtmlHandler)
	//        	{
	//        		if(Arrays.asList(entry.getValue()).contains(uri.getPath()))
	//        		{
	//	        		htmlh	= (HtmlHandler)entry.getKey();
	//	        		break;
	//        		}
	//        	}
	//        }
			
			if (servertuple.getFirstEntity().containsSubhandlerForExactUri(vhost, uri.getPath()))
			{
				return new Future(new IllegalArgumentException("Cannot publish HTML, URI already bound: " + uri.toString()));
			}
	        
	//        if(htmlh==null)
	//        {
			HtmlHandler htmlh	= new HtmlHandler()
		    {
		    	public void service(Request request, Response response)
		    	{
		    		// Hack!!! required for investment planner
		    		// Todo: make accessible to outside
		    		response.addHeader("Access-Control-Allow-Origin", "*");
		    		// http://stackoverflow.com/questions/3136140/cors-not-working-on-chrome
		    		response.addHeader("Access-Control-Allow-Credentials", "true ");
		    		response.addHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
		    		response.addHeader("Access-Control-Allow-Headers", "Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control");
	
		    		super.service(request, response);
		    	}
		    };
		    htmlh.addMapping(vhost, html);
		    
	//		sc.addHttpHandler(htmlh, uri.getPath());
	//        }
	        
	       	servertuple.getFirstEntity().addSubhandler(vhost, uri.getPath(), htmlh);
			
	//		System.out.println("published at: "+uri.getPath());
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return IFuture.DONE;
	}
	
//	/**
//	 *  Publish a resource.
//	 */
////	public IFuture publishResource(URI uri, final ResourceInfo ri)
//	public IFuture publishResource(URI uri, final String type, final String filename)
//	{
//		HttpServer server = getHttpServer(uri, createConfig());
//		
//        ServerConfiguration sc = server.getServerConfiguration();
//		sc.addHttpHandler(new HttpHandler() 
//		{
//            public void service(Request request, Response resp) 
//            {
//            	try
//            	{
//	            	// Set response content type
//	                resp.setContentType(type!=null? type: "text/html");
//	
//	                InputStream is = null;
//	                try
//	        		{
//	        			is = SUtil.getResource0(filename, null);
//	        			OutputStream os = resp.getOutputStream();
//	        			SUtil.copyStream(is, os);
//	        		}
//	        		finally
//	        		{
//	        			try
//	        			{
//	        				if(is!=null)
//	        					is.close();
//	        			}
//	        			catch(Exception e)
//	        			{
//	        			}
//	        		}
//            	}
//            	catch(Exception e)
//            	{
//            		e.printStackTrace();
//            	}
//            }
//        }, uri.getPath());
//		
//		return IFuture.DONE;
//	}
	
	/**
	 *  Publish resources via a rel jar path.
	 *  The resources are searched with respect to the
	 *  component classloader (todo: allow for specifiying RID).
	 */
	public IFuture publishResources(final String uri, final String path)
	{
		final Future	ret	= new Future();
		IComponentIdentifier	cid	= ServiceCall.getLastInvocation()!=null && ServiceCall.getLastInvocation().getCaller()!=null ? ServiceCall.getLastInvocation().getCaller() : component.getId();
		component.getDescription(cid)
			.addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IComponentDescription desc)
			{
				ILibraryService	ls	= component.getFeature(IRequiredServicesFeature.class).getLocalService(new ServiceQuery<>( ILibraryService.class, ServiceScope.PLATFORM));
				ls.getClassLoader(desc.getResourceIdentifier())
					.addResultListener(new ExceptionDelegationResultListener(ret)
				{
					public void customResultAvailable(ClassLoader cl) throws URISyntaxException
					{
						Tuple2 servertuple = getHttpServer(new URI(uri), null);
//				        ServerConfiguration sc = server.getServerConfiguration();
//						sc.addHttpHandler(new CLStaticHttpHandler(cl, path.endsWith("/")? path: path+"/")
//					    {
//					    	public void service(Request request, Response response) throws Exception
//					    	{
//					    		// Hack!!! required for investment planner
//					    		// Todo: make accessible to outside
//				   	    		response.addHeader("Access-Control-Allow-Origin", "*");
//			    	    		// http://stackoverflow.com/questions/3136140/cors-not-working-on-chrome
//			    	    		response.addHeader("Access-Control-Allow-Credentials", "true ");
//			    	    		response.addHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
//			    	    		response.addHeader("Access-Control-Allow-Headers", "Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control");
//					    		super.service(request, response);
//					    	}
//						}, uri.getPath());
						
						servertuple.getFirstEntity().addSubhandler(null, new URI(uri).getPath(), new CLStaticHttpHandler(cl, path.endsWith("/")? path: path+"/")
					    {
					    	public void service(Request request, Response response) throws Exception
					    	{
					    		// Hack!!! required for investment planner
					    		// Todo: make accessible to outside
				   	    		response.addHeader("Access-Control-Allow-Origin", "*");
			    	    		// http://stackoverflow.com/questions/3136140/cors-not-working-on-chrome
			    	    		response.addHeader("Access-Control-Allow-Credentials", "true ");
			    	    		response.addHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
			    	    		response.addHeader("Access-Control-Allow-Headers", "Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control");
					    		super.service(request, response);
					    	}
						});
						
						System.out.println("Resource published at: "+new URI(uri).getPath());
						ret.setResult(null);
					}
				});
			}
		});
		
		return ret;
	}
	
	/**
	 *  Publish file resources from the file system.
	 */
	public IFuture publishExternal(final URI uri, String rootpath)
	{		
		Tuple2 servertuple = getHttpServer(uri, null);
	    StaticHttpHandler handler	= new StaticHttpHandler(rootpath)
	    {
	    	public void service(Request request, Response response) throws Exception
	    	{
	    		// Hack!!! required for investment planner
	    		// Todo: make accessible to outside
   	    		response.addHeader("Access-Control-Allow-Origin", "*");
	    		// http://stackoverflow.com/questions/3136140/cors-not-working-on-chrome
	    		response.addHeader("Access-Control-Allow-Credentials", "true ");
	    		response.addHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
	    		response.addHeader("Access-Control-Allow-Headers", "Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control");
	    		super.service(request, response);
	    	}
	    };
	    handler.setFileCacheEnabled(false);	// see http://stackoverflow.com/questions/13307489/grizzly-locks-static-served-resources
		
//        ServerConfiguration sc = server.getServerConfiguration();
//		sc.addHttpHandler(handler, uri.getPath());
	    servertuple.getFirstEntity().addSubhandler(null, uri.getPath(), handler);
		
//		System.out.println("published at: "+uri.getPath());
		
		return IFuture.DONE;
	}

	
	/**
	 *  Unpublish a service.
	 *  @param sid The service identifier.
	 */
	public IFuture unpublishService(IServiceIdentifier sid)
	{
		Future ret = new Future();
		boolean stopped = false;
		if(sidservers!=null)
		{
			Tuple2 tup = sidservers.remove(sid);
			if(tup!=null)
			{
				HttpServer server = tup.getFirstEntity();
//			    ServerConfiguration config = server.getServerConfiguration();
			    System.out.println("unpub: "+tup.getSecondEntity());
			    
//			    config.removeHttpHandler(tup.getSecondEntity());
//			    if(config.getHttpHandlers().size()==0)
			    Tuple2 servertuple = getHttpServer(tup.getSecondEntity(), null);
				servertuple.getFirstEntity().removeSubhandler(null, tup.getSecondEntity().getPath());
			    if (servertuple.getFirstEntity().isEmpty())
			    {
			    	for(Iterator> servers=portservers.values().iterator(); servers.hasNext(); )
			    	{
			    		if(servers.next().getSecondEntity().equals(server))
			    		{
			    			servers.remove();
			    			break;
			    		}
			    	}
			    	server.shutdownNow();

			    }
			    stopped = true;
				ret.setResult(null);
			}
		}
		if(!stopped)
			ret.setException(new RuntimeException("Published service could not be stopped: "+sid));
		return ret;
	}
	
	/**
	 *  Unpublish an already-published handler.
	 *  @param vhost The virtual host, if any, null for general.
	 *  @param uti The uri being unpublished.
	 */
	public IFuture unpublish(String vhost, URI uri)
	{
		Tuple2 servertuple = getHttpServer(uri, null);
		
		servertuple.getFirstEntity().removeSubhandler(vhost, uri.getPath());
		
		return IFuture.DONE;
	}
	
	/**
	 *  Test if a service is published.
	 */
	public boolean isPublished(IServiceIdentifier sid)
	{
		return sidservers!=null && sidservers.containsKey(sid);
	}
	
	/**
	 *  Starts a server.
	 *  
	 *  @param uri The server URI.
	 *  @param info Publish infos.
	 *  @param errorpagefallback Error page URL fallback if not provided in publish infos.
	 *  @return The server.
	 */
	protected HttpServer startServer(URI uri, PublishInfo info, String errorpagefallback) throws Exception
	{
		HttpServer server = null;
		
		System.out.println("Starting new server: "+uri.getPort());
		
		ErrorPageGenerator epg = null;
		
		String	keystore	= null;
		String	keystorepass	= null;
		if(info!=null)
		{
			for(UnparsedExpression upex: info.getProperties())
			{
//				System.out.println("found publish expression: "+upex.getName());
				
				if("sslkeystore".equals(upex.getName()))
				{
					keystore	= (String)SJavaParser.getParsedValue(upex, null,
						component!=null? component.getFetcher(): null, component!=null? component.getClassLoader(): null);
				}
				else if("sslkeystorepass".equals(upex.getName()))
				{
					keystorepass	= (String)SJavaParser.getParsedValue(upex, null,
						component!=null? component.getFetcher(): null, component!=null? component.getClassLoader(): null);
				}
				else if("errorpage".equals(upex.getName()))
				{
					String errpage = null;
					try
					{
						errpage = (String)SJavaParser.getParsedValue(upex, null,
							component!=null? component.getFetcher(): null, component!=null? component.getClassLoader(): null);
					}
					catch (Exception e)
					{
						errpage = upex.getValue();
					}
					
					if(errpage!=null)
					{
						
						epg = new RedirectErrorPageGenerator(errpage);
					}
				}
			}
		}
		
		if(keystore!=null)
		{
			SSLContextConfigurator sslContext = new SSLContextConfigurator();
			sslContext.setKeyStoreFile(keystore); // contains server keypair
			sslContext.setKeyStorePass(keystorepass);
//			sslContext.setTrustStoreFile("./truststore_server"); // contains client certificate
//			sslContext.setTrustStorePass("asdfgh");
			SSLEngineConfigurator sslConf = new SSLEngineConfigurator(sslContext).setClientMode(false);
			
			server = GrizzlyHttpServerFactory.createHttpServer(uri, (GrizzlyHttpContainer)null, true, sslConf, false);
		}
		else
		{
			server	= GrizzlyHttpServerFactory.createHttpServer(uri, false);
		}
		
		if(epg==null && errorpagefallback != null)
		{
			epg = new RedirectErrorPageGenerator(errorpagefallback);
		}
		
		if(epg!=null)
		{
			server.getServerConfiguration().setDefaultErrorPageGenerator(epg);
		}
		server.start();
		
		if(portservers==null)
			portservers = new HashMap>();
		
		return server;
	}
	
	//-------- helper classes --------
	
	/**
	 *  Main handler dealing with incoming request more intelligently than Grizzly does.
	 *
	 */
	public static class MainHttpHandler extends HttpHandler
	{
		/** Published subhandlers.
		 *  vhost+path -> path+httphandler
		 *  
		 *  Path needs to be preserved in the value since the cache does not preserve it.
		 */
		protected Map, Tuple2> subhandlers;
		
		/** Published subhandler matching cache. */
		protected Map, Tuple2> subhandlercache;
		
		/**
		 *  Create the handler.
		 */
		public MainHttpHandler()
		{
			subhandlers = Collections.synchronizedMap(new HashMap, Tuple2>());
			subhandlercache = Collections.synchronizedMap(new HashMap, Tuple2>());
		}
		
		/**
		 *  Service the request.
		 */
		public void service(Request request, Response resp) throws Exception
		{
			
			String path = request.getRequest().getRequestURIRef().getURI();
			String	host	= request.getHeader("host");
			int	idx	= host.indexOf(":");
			if(idx!=-1)
			{
				host	= host.substring(0, idx);
			}
			
			Tuple2 subhandlertuple = subhandlercache.get(new Tuple2(host, path));
			if (subhandlertuple == null)
			{
				subhandlertuple = subhandlercache.get(new Tuple2(null, path));
			}
			
			int pidx = path.lastIndexOf('/');
			if (subhandlertuple == null && pidx > 0 && pidx <= path.length() - 1)
			{
				String cpath = path.substring(0, pidx);
				subhandlertuple = subhandlercache.get(new Tuple2(host, cpath));
			}
			
			
			if (subhandlertuple == null)
			{
				subhandlertuple = findSubhandler(host, path);
				if (subhandlertuple == null)
				{
					subhandlertuple = findSubhandler(null, path);
				}
				
				if (subhandlertuple != null)
				{
					subhandlercache.put(new Tuple2(host, path), subhandlertuple);
				}
			}
			
			if (subhandlertuple == null)
			{
				throw new RuntimeException("No handler found for path: " + path);
			}
			
			try
			{
				Method setcontextpath = Request.class.getDeclaredMethod("setContextPath", new Class[] { String.class });
				SAccess.setAccessible(setcontextpath, true);
				setcontextpath.invoke(request, subhandlertuple.getFirstEntity());
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			
			subhandlertuple.getSecondEntity().service(request, resp);
		}
		
		/**
		 *  Adds a new subhandler.
		 *  
		 *  @param vhost Virtual host specification.
		 *  @param path Path being handled.
		 *  @param subhandler The subhandler.
		 */
		public void addSubhandler(String vhost, String path, HttpHandler subhandler)
		{
			subhandlers.put(new Tuple2(vhost, path), new Tuple2(path, subhandler));
			synchronized (subhandlers)
			{
				subhandlercache = Collections.synchronizedMap(new HashMap, Tuple2>(subhandlers));
			}
		}
		
		/**
		 *  Tests if a handler for the exact URI is currently published.
		 * 
		 *  @param vhost Virtual host specification.
		 *  @param path Path being handled.
		 *  @return True, if a handler was found.
		 */
		public boolean containsSubhandlerForExactUri(String vhost, String path)
		{
			return subhandlers.containsKey(new Tuple2(vhost, path));
		}
		
		/**
		 *  Tests if the handler contains no subhandlers.
		 *  
		 *  @return True, if no subhandlers remain.
		 */
		public boolean isEmpty()
		{
			return subhandlers.isEmpty();
		}
		
		/**
		 * 
		 * @param vhost Virtual host specification.
		 *  @param path Path being handled.
		 */
		public void removeSubhandler(String vhost, String path)
		{
			subhandlers.remove(new Tuple2(vhost, path));
			synchronized (subhandlers)
			{
				subhandlercache = Collections.synchronizedMap(new HashMap, Tuple2>(subhandlers));
			}			
		}
		
		/**
		 *  Locates an appropriate subhandler that matches the requested resource closely.
		 *  
		 *  @param host The requested virtual host.
		 *  @param path The requested path
		 *  @return The subhandler or null if none is found for the host.
		 */
		protected Tuple2 findSubhandler(String host, String path)
		{
			Tuple2 ret = null;
			do
			{
				int pidx = path.lastIndexOf('/');
				if (pidx >= 0)
				{
					path = path.substring(0, pidx);
					ret = subhandlercache.get(new Tuple2(host, path));
				}
				else
				{
					path = null;
				}
			}
			while (ret == null && path != null && path.length() > 0);
			return ret;
		}
	}
	
	/**
	 *	Allow responding with different htmls based on virtual host name. 
	 */
	// Hack!! only works if no different contexts are published at overlapping resources: e.g. hugo.com/foo/bar vs. dummy.com/foo.
	public static class HtmlHandler extends HttpHandler
	{
		//-------- attributes --------
		
		/** The html responses (host->response). */
		protected Map	mapping;

		//-------- constructors --------
		
		/**
		 *  Create an html handler.
		 */
		public HtmlHandler()
		{
			this.mapping = new LinkedHashMap();
		}
		
		//-------- methods --------
		
		/**
		 *  Add a mapping.
		 */
		public void	addMapping(String host, String html)
		{
			this.mapping.put(host, html);
		}
		
		//-------- HttpHandler methods --------

		/**
		 *  Service the request.
		 */
		public void service(Request request, Response resp) 
		{
			String	host	= request.getHeader("host");
			int	idx	= host.indexOf(":");
			if(idx!=-1)
			{
				host	= host.substring(0, idx);
			}
			String	html	= null;
			
			for(Map.Entry entry: mapping.entrySet())
			{
				if(entry.getKey().equals(host))
				{
					html	= entry.getValue();
					break;
				}
				else if(html==null)
				{
					// Use first entry, when no other match is found.
					html	= entry.getValue(); 
				}
			}
			
			try
			{
		    	// Set response content type
		        resp.setContentType("text/html");

		        // Actual logic goes here.
		        Writer out = resp.getWriter();
		        out.write(html);
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
	
	public static class RedirectErrorPageGenerator implements ErrorPageGenerator
	{
		/** Redirect URL. */
		protected String redirecturl;
		
		/**
		 *  Creates the Generator. 
		 */
		public RedirectErrorPageGenerator(String redirecturl)
		{
			this.redirecturl = redirecturl;
		}
		
		/**
		 *  Returns the redirect URL.
		 *  @return The redirect URL.
		 */
		public String getRedirectUrl()
		{
			return redirecturl;
		}
		
		/**
		 *  Generates the error page with a redirect.
		 */
        public String generate(Request request, int status, String reasonPhrase, String description, Throwable exception) 
        {
       	 return "";
        }
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy