Add an implementation for Services.getServiceInfo method and categorization for services.
[jabaws.git] / webservices / compbio / ws / server / RegistryWS.java
1 /* Copyright (c) 2011 Peter Troshin\r
2  *  \r
3  *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 2.0     \r
4  * \r
5  *  This library is free software; you can redistribute it and/or modify it under the terms of the\r
6  *  Apache License version 2 as published by the Apache Software Foundation\r
7  * \r
8  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\r
9  *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache \r
10  *  License for more details.\r
11  * \r
12  *  A copy of the license is in apache_license.txt. It is also available here:\r
13  * @see: http://www.apache.org/licenses/LICENSE-2.0.txt\r
14  * \r
15  * Any republication or derived work distributed in source code form\r
16  * must include this copyright and license notice.\r
17  */\r
18 package compbio.ws.server;\r
19 \r
20 import java.io.PrintWriter;\r
21 import java.io.StringWriter;\r
22 import java.io.Writer;\r
23 import java.util.Date;\r
24 import java.util.Map;\r
25 import java.util.Set;\r
26 import java.util.concurrent.ConcurrentHashMap;\r
27 \r
28 import javax.annotation.Resource;\r
29 import javax.jws.WebService;\r
30 import javax.servlet.http.HttpServletRequest;\r
31 import javax.xml.ws.WebServiceContext;\r
32 import javax.xml.ws.handler.MessageContext;\r
33 \r
34 import org.apache.log4j.Logger;\r
35 \r
36 import compbio.data.msa.JABAService;\r
37 import compbio.ws.client.Services;\r
38 import compbio.ws.client.WSTester;\r
39 \r
40 /**\r
41  * JABAWS services registry\r
42  * \r
43  * @author pvtroshin\r
44  * \r
45  */\r
46 @WebService(endpointInterface = "compbio.data.msa.RegistryWS", targetNamespace = JABAService.V2_SERVICE_NAMESPACE, serviceName = "RegistryWS")\r
47 public class RegistryWS implements compbio.data.msa.RegistryWS, JABAService {\r
48 \r
49         // Ask for resource injection\r
50         @Resource\r
51         WebServiceContext wsContext;\r
52 \r
53         private static Logger log = Logger.getLogger(RegistryWS.class);\r
54 \r
55         /**\r
56          * Stores tested and passed (the test) services and their testing time\r
57          */\r
58         private final static Map<Services, Date> operating = new ConcurrentHashMap<Services, Date>();\r
59 \r
60         /**\r
61          * Indicate whether the services were tested at all\r
62          */\r
63         private static boolean allTested = false;\r
64 \r
65         @Override\r
66         public Set<Services> getSupportedServices() {\r
67                 init();\r
68                 return operating.keySet();\r
69         }\r
70 \r
71         private void init() {\r
72                 // Do not allow tests to run concurrently\r
73                 if (timeToTest()) {\r
74                         synchronized (operating) {\r
75                                 if (timeToTest()) {\r
76                                         testAllServices();\r
77                                         allTested = true;\r
78                                 }\r
79                         }\r
80                 }\r
81         }\r
82 \r
83         private boolean timeToTest() {\r
84                 if (!allTested) {\r
85                         return true;\r
86                 }\r
87                 // 24 h\r
88                 if (getLongestUntestedServiceTime() > 3600 * 24) {\r
89                         return true;\r
90                 }\r
91                 return false;\r
92         }\r
93 \r
94         /**\r
95          * Return time in seconds for the test for the oldest unchecked service\r
96          * \r
97          * @return\r
98          */\r
99         private int getLongestUntestedServiceTime() {\r
100                 int timePassed = 0;\r
101                 for (Services serv : operating.keySet()) {\r
102                         int lasttimepassed = getLastTested(serv);\r
103                         if (timePassed < lasttimepassed) {\r
104                                 timePassed = lasttimepassed;\r
105                         }\r
106                 }\r
107                 return timePassed;\r
108         }\r
109 \r
110         @Override\r
111         public int getLastTested(Services service) {\r
112                 Date testedOn = getLastTestedOn(service);\r
113                 if (testedOn != null) {\r
114                         return (int) ((System.currentTimeMillis() - testedOn.getTime()) / 1000);\r
115                 }\r
116                 return 0;\r
117         }\r
118 \r
119         /**\r
120          * Can potentially return null if the service has not been tested yet.\r
121          */\r
122         @Override\r
123         public Date getLastTestedOn(Services service) {\r
124                 if (operating.containsKey(service)) {\r
125                         return operating.get(service);\r
126                 }\r
127                 return null;\r
128         }\r
129 \r
130         /**\r
131          * TODO improve reporting. stop testing service on unsupported runtime env\r
132          * exception\r
133          */\r
134         @Override\r
135         public String testAllServices() {\r
136                 Writer testlog = new StringWriter();\r
137                 PrintWriter writer = new PrintWriter(testlog, true);\r
138                 WSTester tester = new WSTester(getServicePath(), writer);\r
139                 // This is done deliberately to prevent malicious user from overloading\r
140                 // the server\r
141                 synchronized (operating) {\r
142                         for (Services service : Services.values()) {\r
143                                 try {\r
144                                         if (tester.checkService(service)) {\r
145                                                 operating.put(service, new Date());\r
146                                         }\r
147                                 } catch (Exception e) {\r
148                                         log.info(e, e.getCause());\r
149                                         writer.println("Fails to connect to a web service: "\r
150                                                         + service + " With " + e.getLocalizedMessage()\r
151                                                         + "\nDetails: ");\r
152                                         e.printStackTrace(writer);\r
153                                 }\r
154                         }\r
155                 }\r
156                 writer.close();\r
157                 return testlog.toString();\r
158         }\r
159 \r
160         private String getServicePath() {\r
161                 assert wsContext != null : "WS context injection failed!";\r
162                 MessageContext msContext = wsContext.getMessageContext();\r
163                 HttpServletRequest request = (HttpServletRequest) msContext\r
164                                 .get(MessageContext.SERVLET_REQUEST);\r
165 \r
166                 StringBuffer server = request.getRequestURL();\r
167                 server = server.delete(server.lastIndexOf("/"), server.length());\r
168                 return server.toString();\r
169         }\r
170 \r
171         @Override\r
172         public String testService(Services service) {\r
173                 String server = getServicePath();\r
174                 Writer testlog = new StringWriter();\r
175                 PrintWriter writer = new PrintWriter(testlog, true);\r
176                 WSTester tester = new WSTester(server, writer);\r
177                 try {\r
178                         synchronized (operating) {\r
179                                 boolean succeed = tester.checkService(service);\r
180                                 if (succeed) {\r
181                                         operating.put(service, new Date());\r
182                                 }\r
183                         }\r
184                 } catch (Exception e) {\r
185                         log.info(e, e.getCause());\r
186                         writer.println("Fails to connect to a web service: " + service\r
187                                         + " With " + e.getLocalizedMessage() + "\nDetails: ");\r
188                         e.printStackTrace(writer);\r
189                 } finally {\r
190                         writer.close();\r
191                 }\r
192                 return testlog.toString();\r
193         }\r
194         @Override\r
195         public boolean isOperating(Services service) {\r
196                 init();\r
197                 return operating.containsKey(service);\r
198         }\r
199 \r
200         @Override\r
201         public String getServiceDescription(Services service) {\r
202                 return service.getServiceInfo();\r
203         }\r
204 \r
205         @Override\r
206         public Set<Category> getServiceCategories() {\r
207                 return Category.getCategories();\r
208         }\r
209 \r
210 }\r