View Javadoc

1   /*
2   Copyright (c) 2006, University of Tromsø
3   All rights reserved.
4   
5   Redistribution and use in source and binary forms, with or without 
6   modification, are permitted provided that the following conditions are met:
7   
8    * Redistributions of source code must retain the above copyright notice, this list 
9      of conditions and the following disclaimer.
10  
11   * Redistributions in binary form must reproduce the above copyright notice, this 
12     list of conditions and the following disclaimer in the documentation and/or other 
13     materials provided with the distribution.
14  
15   * Neither the name of the University of Tromsø nor the names of its contributors may 
16     be used to endorse or promote products derived from this software without specific 
17     prior written permission.
18  
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
20  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
21  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
22  SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
23  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
24  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
25  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
26  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
27  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
28  DAMAGE.
29  */
30  
31  package argos.bangbang.xmlrpc;
32  
33  import java.lang.reflect.Method;
34  import java.util.ArrayList;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.logging.Level;
39  import java.util.logging.Logger;
40  
41  import javax.management.Notification;
42  
43  import org.apache.xmlrpc.XmlRpcException;
44  import org.apache.xmlrpc.XmlRpcHandler;
45  import org.apache.xmlrpc.XmlRpcRequest;
46  import org.mortbay.jetty.servlet.Context;
47  import org.mortbay.jetty.servlet.ServletHolder;
48  
49  import argos.annotation.Description;
50  import argos.annotation.Impact;
51  import argos.annotation.Init;
52  import argos.annotation.NotificationHandler;
53  import argos.annotation.RemoveInstrumentation;
54  import argos.annotation.ServiceMeta;
55  import argos.bangbang.jetty6.Jetty;
56  import argos.bangbang.webservice.annotation.WebMethod;
57  import argos.deploy.ComponentManager;
58  import argos.metadata.ComponentMetaInfo;
59  import argos.metadata.ServiceMetaInfo;
60  import argos.naming.NamingService;
61  
62  
63  /**
64   * Created on 03.aug.2006
65   * 
66   * @author Dan Peder Eriksen
67   */
68  @Description("Adds XML-RPC as a bangbang service to Argos.")
69  public class XmlRpc implements XmlRpcHandler {
70  	private static final Logger logger = Logger.getLogger(XmlRpc.class.getName());
71  	
72  	@ServiceMeta public ServiceMetaInfo meta;
73  	private Map<String, Method> methodCache;
74  	private Map<String, Object> components;
75  	
76  	public XmlRpc() {
77  		super();
78  		methodCache = new HashMap<String, Method>();
79  		components = new HashMap<String, Object>();
80  	}
81  	
82  	@Init
83  	@Description("Intigrates XML-RPC into Argos.")
84  	@Impact(Impact.ACTION)
85  	public void init() throws Exception {
86  		Jetty jetty = (Jetty) NamingService.getInstance().getComponentByName("!!Jetty6");
87  		Context con = new Context(jetty.getJettyServer(), "/xmlrpc");
88  		con.addServlet(new ServletHolder(RPCServlet.class), "/*");
89  		jetty.addContext(con);
90  	}
91  	
92  	@Description("Adds a new component as a webService.")
93  	@Impact(Impact.ACTION)
94  	public void addComponent(ComponentMetaInfo componentMeta) {
95  		Object component = NamingService.getInstance().getComponentByMeta(componentMeta);
96  		String name = componentMeta.getName();
97  		components.put(name, component);
98  		
99  		for(Method method : component.getClass().getMethods()) {
100 			if(method.isAnnotationPresent(WebMethod.class)) {
101 				if(methodCache.containsKey(name + "." + method.getName())) {
102 					logger.warning("Method " + method.getName() + 
103 							" already added, ignoring other implementation.");
104 				}
105 				else {
106 					methodCache.put(name + "." + method.getName(), method);
107 					logger.info("Adding XML-RPC method " + name + "." + method.getName() + "().");
108 				}
109 			}
110 		}
111 	}
112 	
113 	@Description("Removes a component from web services.")
114 	@Impact(Impact.ACTION)
115 	public void removeComponent(ComponentMetaInfo componentMeta) {
116 		String name = componentMeta.getName();
117 		components.remove(name);
118 		List<String> remove = new ArrayList<String>();
119 		for(String key : methodCache.keySet()) {
120 			if(key.startsWith(name + ".")) {
121 				if(remove.add(key)) {
122 					logger.info("Removing XML-RPC method " + key + "().");
123 				}
124 			}
125 		}
126 		for(String key : remove) {
127 			methodCache.remove(key);
128 		}
129 	}
130 	
131 	@Description("Invoked for calling the web sevice.")
132 	@Impact(Impact.ACTION_INFO)
133 	public Object execute(XmlRpcRequest request) throws XmlRpcException {
134 		String methodName = request.getMethodName();
135 		Object[] params = new Object[request.getParameterCount()];
136 		for(int i = 0; i < request.getParameterCount(); i++) {
137 			params[i] = request.getParameter(i);
138 		}
139 		
140 		Method method = methodCache.get(methodName);
141 		if(method == null) {
142 			logger.warning("Tried calling " + methodName + "which doesnt exist " +
143 					"or isnt exposed with XML-RPC.");
144 			throw new XmlRpcException("The method " + methodName + " is not exposed with XML-RPC.");
145 		}
146 		else {
147 			try {
148 				return method.invoke(components.get(methodName.substring(0, 
149 						methodName.indexOf('.'))), params);
150 			}
151 			catch(Exception e) {
152 				logger.log(Level.SEVERE, "Unable to call xml-rpc method " + methodName, e);
153 				throw new XmlRpcException("Exception when calling method " + methodName + ": " + 
154 						e.getMessage(), e);
155 			}
156 		}
157 	}
158 	
159 	@RemoveInstrumentation
160 	@NotificationHandler
161 	public void handler(Notification not) {
162 		if(handle(not)) {
163 			ServiceMetaInfo temp = (ServiceMetaInfo) not.getUserData();
164 			
165 			if(temp.dependsOn(meta)) {
166 				if(not.getType().equals(ComponentManager.SERVICE_STARTED)) {
167 					for(ComponentMetaInfo componentMeta : ((ServiceMetaInfo) not.getUserData()).getComponentMetaInfo()) {
168 						addComponent(componentMeta);	
169 					}
170 					
171 				}
172 				else if(not.getType().equals(ComponentManager.SERVICE_STOPPED)) {
173 					for(ComponentMetaInfo componentMeta : ((ServiceMetaInfo) not.getUserData()).getComponentMetaInfo()) {
174 						removeComponent(componentMeta);	
175 					}
176 				}
177 			}
178 		}
179 	}
180 	
181 	private boolean handle(Notification not) {
182 		return (not.getType().equals(ComponentManager.SERVICE_STARTED) ||
183 				not.getType().equals(ComponentManager.SERVICE_STOPPED));
184 	}
185 }