View Javadoc
1   /*
2    * Copyright 2001-2011 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *      http://www.apache.org/licenses/LICENSE-2.0
8    * 
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.apache.juddi.v3.client.mapping;
16  
17  import java.net.BindException;
18  import java.net.URL;
19  import java.rmi.RemoteException;
20  import java.util.ArrayList;
21  import java.util.Properties;
22  import java.util.concurrent.ConcurrentHashMap;
23  
24  import javax.xml.namespace.QName;
25  
26  import org.apache.commons.configuration.ConfigurationException;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.juddi.api_v3.AccessPointType;
30  import org.apache.juddi.v3.client.ClassUtil;
31  import org.apache.juddi.v3.client.config.UDDIClerk;
32  import org.apache.juddi.v3.client.config.UDDIKeyConvention;
33  import org.apache.juddi.v3.client.subscription.SubscriptionCallbackListener;
34  import org.apache.juddi.v3.client.transport.TransportException;
35  import org.uddi.api_v3.AccessPoint;
36  import org.uddi.api_v3.BindingTemplate;
37  import org.uddi.api_v3.BindingTemplates;
38  import org.uddi.api_v3.BusinessService;
39  
40  /**
41   * The ServiceLocator contacts the UDDI registry to lookup an Endpoint given a UDDI ServiceKey.<br>
42   * This class does NOT chase down WSDL, hosting redirectors or other binding references from
43   * access point useType values. See 
44   * {@link org.apache.juddi.v3.client.config.UDDIClerk#getEndpoints UDDIClerk.getEndpoints}
45   * @see SubscriptionCallbackListener
46   * @author <a href="mailto:kstam@apache.org">Kurt T Stam</a>
47   */
48  public class ServiceLocator {
49  	
50  	private static final Log log = LogFactory.getLog(ServiceLocator.class);
51  	
52  	private UDDIClerk clerk;
53  	private Properties properties = new Properties();
54  	private UDDIServiceCache serviceCache = null;
55  	private String policy = null;
56  	private SelectionPolicy selectionPolicy = null;
57  	private URLLocalizer urlLocalizer = null;
58  	private ConcurrentHashMap<String, Topology> simpleCache= null;
59  	/**
60  	 * Requirement in the config is a clerk with access credentials to the UDDI server
61  	 * you want the locator to do lookups to. When a live cache is used the clerk
62  	 * will register a callback into this UDDI server and the clerk will therefore need
63  	 * inquiry and publish access. The credentials can be set in the uddi-client.xml
64  	 * configuration file.
65  	 *  
66  	 * @param clerk a UDDI Clerk with publish access to the UDDI Server.
67  	 * @throws ConfigurationException
68  	 */
69  	public ServiceLocator(UDDIClerk clerk) {
70  		super();
71  		this.clerk = clerk;
72  		properties =clerk.getUDDINode().getProperties();
73  	}
74  	
75  	public ServiceLocator(UDDIClerk clerk, URLLocalizer urlLocalizer, Properties properties) throws ConfigurationException	{
76  		super();
77  		this.clerk = clerk;
78  		this.urlLocalizer = urlLocalizer;
79  		this.properties = properties;
80  		if (properties == null) 
81                          this.properties = clerk.getUDDINode().getProperties();
82  	}
83  	
84          /**
85  	 * Creates a new UDDIServiceCache, which brings up a new WebService Endpoint. This
86  	 * EndPoint will be called by the UDDI server if any service changes. A callback
87  	 * will result in cleaning the cache. 
88  	 * 
89  	 * @param baseCallbackURL
90  	 * @throws ConfigurationException
91  	 */
92  	public ServiceLocator withCache(URL baseCallbackURL) throws ConfigurationException {
93  		if (serviceCache == null) {
94  			serviceCache = initCache(baseCallbackURL);
95  		}
96  		return this;
97  	}
98  	
99  	/**
100 	 * A live cache will receive callbacks from the UDDI server in case there is an update to
101 	 * a service's bindings. All callbacks will clear the UDDIClientCache ensuring that subsequent
102 	 * lookups will contact the UDDI server for the latest binding information.
103 	 * 
104 	 * The baseCallbackURL can be set to solve binding issue. If
105 	 * 
106 	 * @param baseCallbackURL
107 	 * @return a service locator object
108 	 * @throws ConfigurationException
109 	 * @throws BindException
110 	 */
111 	public ServiceLocator withLiveCache(URL baseCallbackURL) throws ConfigurationException, BindException {
112 		if (serviceCache == null) {
113 			serviceCache = initCache(baseCallbackURL);
114 			serviceCache.registerAsMBean();
115 		}
116 		if (! serviceCache.hasListener()) {
117 			serviceCache.publishAndRegisterHttpCallbackEndpoint();
118 		}
119 		return this;
120 	}
121         
122         
123         public ServiceLocator withSimpleCache(){
124                 if (simpleCache==null){
125                        simpleCache= new ConcurrentHashMap<String, Topology>();
126                 }
127                 return this;
128         }
129 	
130 	public UDDIServiceCache getUDDIServiceCache() {
131 		return serviceCache;
132 	}
133 	
134 	/**
135 	 * The policy selection can be set as property "juddi.client.selection.policy"
136 	 * or it can be set programmatically using this method. A Policy is a class which 
137 	 * implements the SelectionPolicy interface. Known implementations are
138 	 * org.apache.juddi.v3.client.mapping.PolicyLocalFirst and 
139 	 * org.apache.juddi.v3.client.mapping.PolicyRoundRobin. If the policy is not
140 	 * set the default org.apache.juddi.v3.client.mapping.PolicyLocalFirst is used.
141 	 * 
142 	 * @param policy - the desired policy.
143 	 * @return ServiceLocator
144 	 * @see org.apache.juddi.v3.client.mapping.PolicyLocalFirst
145 	 * @see org.apache.juddi.v3.client.mapping.PolicyRoundRobin
146 	 */
147 	public ServiceLocator setPolicy(String policy) {
148 		this.policy = policy;
149 		return this;
150 	}
151 	/**
152 	 * Returns the selection policy in use by this instance of the ServiceLocator.
153 	 * 
154 	 * @return SelectionPolicy - the selection policy.
155 	 * @throws ConfigurationException
156 	 */
157 	public SelectionPolicy getPolicy() throws ConfigurationException {
158 		try {
159 			if (selectionPolicy==null) {
160 				if (policy==null) {
161                                         //TODO update .NET schema file to support this
162 					policy = properties.getProperty("juddi.client.selection.policy", "org.apache.juddi.v3.client.mapping.PolicyLocalFirst");
163 				}
164 				@SuppressWarnings("unchecked")
165 				Class<? extends SelectionPolicy> selectionPolicyClass = (Class<? extends SelectionPolicy>)
166 					ClassUtil.forName(policy, this.getClass());
167 				selectionPolicy =  selectionPolicyClass.getConstructor(Properties.class).newInstance(properties);
168 			}
169 			return selectionPolicy;
170 		} catch (Exception e) {
171 			throw new ConfigurationException(e.getMessage(),e);
172 		}
173 	}
174 	
175 	/**
176 	 * Creates a new UDDIServiceCache, which brings up a new WebService Endpoint. This
177 	 * EndPoint will be called by the UDDI server if any service changes. A callback
178 	 * will result in cleaning the cache. 
179 	 * 
180 	 * @param baseCallbackURL
181 	 * @throws ConfigurationException
182 	 */
183 	private UDDIServiceCache initCache(URL baseCallbackURL) throws ConfigurationException {
184 		if (clerk==null) throw new ConfigurationException("The UDDIClerk is needed to use the UDDIServiceCache and is null");
185 		if (urlLocalizer==null) urlLocalizer = new URLLocalizerDefaultImpl(baseCallbackURL);
186                 if (properties==null) properties = new Properties();
187 		try {
188 			log.info("Creating a UDDICLientCache");
189 			return new UDDIServiceCache(clerk, urlLocalizer, properties);
190 		} catch (Exception e) {
191 			throw new ConfigurationException(e.getMessage(),e);
192 		}
193 	}
194         
195         public void clearCaches(){
196                 if (serviceCache!=null)
197                         serviceCache.removeAll();
198                 if (simpleCache!=null)
199                         simpleCache.clear();
200         }
201 	
202 	/**
203 	 * 
204 	 * @throws RemoteException
205 	 * @throws ConfigurationException
206 	 * @throws TransportException
207 	 */
208 	public void shutdown() throws RemoteException, ConfigurationException, TransportException {
209                 if (serviceCache!=null)
210                         serviceCache.shutdown();
211 	}
212 	/**
213 	 * Looks up the Endpoints for a Service. If the cache is in use it will try to 
214 	 * obtain them from the cache. If no Endpoints are found, or if the cache is not
215 	 * in use, the clerk will do a lookup for this service. After Endpoints are found
216 	 * it will use a policy to pick one Endpoint to return. Returns null if no endpoints
217 	 * are found.
218 	 * 
219 	 * @param serviceKey
220 	 * @return endpoint
221 	 * @throws RemoteException
222 	 * @throws ConfigurationException
223 	 * @throws TransportException
224 	 */
225 	public String lookupEndpoint(String serviceKey) throws RemoteException, ConfigurationException, TransportException {
226 		Topology topology = null;
227                 if (simpleCache != null && simpleCache.containsKey(serviceKey)){
228                         topology = simpleCache.get(serviceKey);
229                 } else if (serviceCache==null) { //nocache in use
230 			topology = lookupEndpointInUDDI(serviceKey);
231 		} else { //with cache
232 			//try to get it from the cache first
233 			topology = serviceCache.lookupService(serviceKey);
234 			if (topology == null) { //not found in the cache
235 				topology = lookupEndpointInUDDI(serviceKey);
236 			}
237 		}
238                 if (topology!=null && topology.getEprs().size() > 0) {
239                         if (simpleCache!=null){
240                                 simpleCache.put(serviceKey,topology);
241                         }
242                         String epr = getPolicy().select(topology);
243                         return epr;
244                 } 
245                 return null;
246 	}
247 	
248 	/** 
249 	 * Looks up the Endpoints for a Service. If the cache is in use it will try to 
250 	 * obtain them from the cache. If no Endpoints are found, or if the cache is not
251 	 * in use, the clerk will do a lookup for this service. After Endpoints are found
252 	 * it will use a policy to pick one Endpoint to return. Returns null if no endpoints
253 	 * are found.
254 	 * 
255 	 * @param serviceQName
256 	 * @param portName
257 	 * @return Returns null if no endpoints
258 	 * are found.
259 	 * @throws TransportException 
260 	 * @throws ConfigurationException 
261 	 * @throws RemoteException 
262 	 */
263 	public String lookupEndpoint(QName serviceQName, String portName) throws RemoteException, ConfigurationException, TransportException {
264 		String serviceKey = UDDIKeyConvention.getServiceKey(properties, serviceQName.getLocalPart());
265 		return lookupEndpoint(serviceKey);
266 	}
267 	
268 	private Topology lookupEndpointInUDDI(String serviceKey) throws RemoteException, ConfigurationException, TransportException {
269 		Topology topology = null;
270 		
271 		BusinessService service = clerk.getServiceDetail(serviceKey);
272 		if (service==null) {
273 			log.warn("No Service with key " + serviceKey + " was found in the registry.");
274 			//TODO find service by tModel
275 		}
276 		if (service!=null && service.getBindingTemplates()!=null && service.getBindingTemplates().getBindingTemplate() != null) {
277 			ArrayList<String> eprs = new ArrayList<String>();
278 			BindingTemplates bindingTemplates = service.getBindingTemplates();
279 			if (bindingTemplates==null) {
280 				log.warn("Found service " + service.getName().get(0).getValue()
281 						  + " with serviceKey '" + serviceKey + "'" 
282 						  + " but no EPRs");
283 			} else {
284 				log.debug("Found service " + service.getName().get(0).getValue()
285 						  + " with serviceKey '" + serviceKey + "'" 
286 						  + " and " + bindingTemplates.getBindingTemplate().size() + " EPRs");
287 				//Loop over all bindingTemplates found and get the endpoints.
288 				for (BindingTemplate bindingTemplate : bindingTemplates.getBindingTemplate()) {
289 					AccessPoint accessPoint = bindingTemplate.getAccessPoint();
290                                         if (accessPoint!=null){
291 					if (AccessPointType.END_POINT.toString().equals(accessPoint.getUseType())) {
292 						String url = accessPoint.getValue();
293 						log.debug("epr= " + url);
294 						eprs.add(url);
295 					} else if(AccessPointType.WSDL_DEPLOYMENT.toString().equals(accessPoint.getUseType())) {
296                                                 //do something here
297                                                 //try to open that wsdl, then grab the endpoints
298                                                 //String url=fetchFromWsdl(accessPoint.getValue());
299                                               
300 					}  else if(AccessPointType.BINDING_TEMPLATE.toString().equals(accessPoint.getUseType())) {
301                                                 //do something here
302                                                 //grab that binding template and use that address
303 					}
304                                                 }
305 				}
306 				if (eprs.size()>0) {
307 					topology = new Topology(eprs);
308 				}
309 			}
310 		}
311 		if (serviceCache!=null && topology!=null) { //add to cache
312 			serviceCache.addService(serviceKey, topology);
313 		}
314 		return topology;
315 	}
316 	
317 	
318 }