001 /* 002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org 003 * Copyright (C) 2011 NightLabs Consulting GmbH 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 package org.cumulus4j.keymanager.channel; 019 020 import java.io.BufferedReader; 021 import java.io.InputStream; 022 import java.io.InputStreamReader; 023 import java.io.StringWriter; 024 import java.net.URI; 025 import java.net.URL; 026 027 import javax.ws.rs.core.MediaType; 028 029 import org.cumulus4j.keymanager.back.shared.ErrorResponse; 030 import org.cumulus4j.keymanager.back.shared.JAXBContextResolver; 031 import org.cumulus4j.keymanager.back.shared.NullResponse; 032 import org.cumulus4j.keymanager.back.shared.Request; 033 import org.cumulus4j.keymanager.back.shared.Response; 034 import org.slf4j.Logger; 035 import org.slf4j.LoggerFactory; 036 037 import com.sun.jersey.api.client.Client; 038 import com.sun.jersey.api.client.ClientResponse; 039 import com.sun.jersey.api.client.UniformInterfaceException; 040 import com.sun.jersey.api.client.WebResource; 041 import com.sun.jersey.api.client.config.ClientConfig; 042 import com.sun.jersey.api.client.config.DefaultClientConfig; 043 044 /** 045 * <p> 046 * Thread that listens to incoming {@link Request}s and processes them. 047 * </p> 048 * 049 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 050 */ 051 public class KeyManagerChannelListenerThread 052 extends Thread 053 { 054 private static final Logger logger = LoggerFactory.getLogger(KeyManagerChannelListenerThread.class); 055 private KeyManagerChannelManager keyManagerChannelManager; 056 057 /** 058 * Instantiate a new listener thread. 059 * @param keyManagerChannelManager the manager which instantiates this thread and manages 060 * the {@link RequestHandler}s to dispatch the incoming requests to. 061 */ 062 public KeyManagerChannelListenerThread(KeyManagerChannelManager keyManagerChannelManager) 063 { 064 if (keyManagerChannelManager == null) 065 throw new IllegalArgumentException("keyManagerChannelManager == null"); 066 067 this.keyManagerChannelManager = keyManagerChannelManager; 068 setDaemon(true); 069 } 070 071 private volatile boolean interruptForced; 072 073 @Override 074 public void interrupt() { 075 interruptForced = true; 076 super.interrupt(); 077 } 078 079 @Override 080 public boolean isInterrupted() { 081 return super.isInterrupted() || interruptForced; 082 } 083 084 private Client client; 085 private URI nextRequestURI = null; 086 087 @Override 088 public void run() { 089 Response response = null; 090 while (!isInterrupted()) { 091 try { 092 if (keyManagerChannelManager.unregisterThreadIfMoreThanDesiredThreadCount(this)) 093 return; 094 095 if (client == null) { 096 ClientConfig clientConfig = new DefaultClientConfig(JAXBContextResolver.class); 097 client = Client.create(clientConfig); 098 } 099 100 if (nextRequestURI == null) { 101 String s = keyManagerChannelManager.getKeyManagerChannelURL().toString(); 102 if (!s.endsWith("/")) 103 s += '/'; 104 105 nextRequestURI = new URL(s + "nextRequest/" + keyManagerChannelManager.getSessionManager().getCryptoSessionIDPrefix()).toURI(); 106 } 107 108 WebResource.Builder nextRequestWebResourceBuilder = client.resource( 109 nextRequestURI 110 ).type(MediaType.APPLICATION_XML_TYPE).accept(MediaType.APPLICATION_XML_TYPE); 111 112 if (response == null) 113 response = new NullResponse(); // It seems Jersey does not allow null as entity :-( 114 115 Request request; 116 try { 117 request = nextRequestWebResourceBuilder.post(Request.class, response); 118 } catch (UniformInterfaceException x) { 119 // Unfortunately, the Jersey client does not simply return null, but throws this exception, 120 // if the service returns null. Hence we catch it and check for code 204 (NO_CONTENT), which is no 121 // error, but expected. Alternatively, we could introduce a NullRequest (similar to the NullResponse), 122 // but I think, checking for 204 is cleaner than sending dummy objects around. Marco :-) 123 if (x.getResponse().getStatus() == ClientResponse.Status.NO_CONTENT.getStatusCode()) 124 request = null; 125 else 126 throw x; 127 } 128 129 response = null; // we processed the last response (if any) and thus have to clear this now to prevent sending it again. 130 131 if (request != null) { 132 try { 133 RequestHandler<Request> requestHandler = keyManagerChannelManager.getRequestHandler(request); 134 response = requestHandler.handle(request); 135 } catch (Throwable t) { 136 logger.error("run: " + t, t); 137 response = new ErrorResponse(request, t); 138 } 139 140 if (response == null) 141 response = new NullResponse(request); 142 } 143 } catch (UniformInterfaceException x) { 144 try { 145 InputStream in = x.getResponse().getEntityInputStream(); 146 BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8")); 147 StringWriter sw = new StringWriter(); 148 String s; 149 while (null != (s = r.readLine())) 150 sw.append(s); 151 152 in.close(); 153 logger.error("run: " + x + "\n" + sw, x); 154 } catch (Exception y) { 155 logger.error("run: Caught exception while processing UniformInterfaceException: " + y, y); 156 } 157 try { Thread.sleep(5000); } catch (InterruptedException e) { doNothing(); } // prevent hammering on the server 158 } catch (Exception x) { 159 logger.error("run: " + x, x); 160 try { Thread.sleep(5000); } catch (InterruptedException e) { doNothing(); } // prevent hammering on the server 161 } 162 } 163 } 164 165 private static final void doNothing() { } 166 }