001 package edu.nrao.sss.util; 002 003 import java.io.StringReader; 004 import java.net.URL; 005 import java.util.List; 006 import java.util.ArrayList; 007 import java.rmi.RemoteException; 008 import javax.xml.bind.JAXBElement; 009 import javax.xml.namespace.QName; 010 011 import org.apache.log4j.Logger; 012 import org.apache.axis.client.Call; 013 import org.apache.axis.client.Service; 014 015 import edu.nrao.sss.astronomy.sesame.Resolver; 016 import edu.nrao.sss.astronomy.sesame.Sesame; 017 import edu.nrao.sss.astronomy.sesame.Target; 018 019 /** 020 * The SourceNameResolver encapsulates the networking and XML envolved in 021 * making a source name lookup at the CDS and provides quick javabeans methods 022 * for retreiving a particular bit of information about a source. 023 * 024 * @author btruitt 025 * 026 */ 027 public class SourceNameResolver 028 { 029 private static final Logger log = Logger.getLogger(SourceNameResolver.class); 030 031 private Call call = null; 032 private Sesame data = null; 033 034 private static final String[] serviceUrls = { 035 "http://cdsws.u-strasbg.fr/axis/services/Sesame", 036 "http://vizier.hia.nrc.ca:8080/axis/services/Sesame", 037 "http://vizier.nao.ac.jp:8080/axis/services/Sesame", 038 }; 039 040 private static int CURRENT_RESOURCE = 0; 041 042 public SourceNameResolver() 043 { 044 try 045 { 046 //Create a service to do the work. 047 Service service = new Service(); 048 049 this.call = (Call)service.createCall(); 050 051 //set the address, method, and parameters of the call. Also specify their types. 052 this.call.setTargetEndpointAddress(new URL(serviceUrls[CURRENT_RESOURCE])); 053 054 this.call.setOperationName(new QName("sesame")); 055 056 this.call.addParameter("name", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN); 057 this.call.addParameter("resultType", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN); 058 this.call.addParameter("all", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN); 059 this.call.addParameter("service", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN); 060 061 this.call.setReturnType(org.apache.axis.Constants.XSD_STRING); 062 063 //Shorten the timeout to something more reasonable.(in milliseconds) 064 this.call.setTimeout(new Integer(15000)); 065 } 066 067 //Any other exception we'll just have to barf on and assume we can't 068 //get the source because something else is wrong. 069 catch (Exception e) 070 { 071 throw new IllegalStateException(e); 072 } 073 } 074 075 /** 076 * Does a name lookup at one of the sites in {@link #serviceUrls} and 077 * returns an object that has convenient java bean methods for accessing 078 * properties of a source. The urls in the serviceUrls array are tried in 079 * order (in the event that the service is down at the first site, the 2nd 080 * is tried, etc.). 081 * 082 * @throws SourceNotFoundException if <code>target</code> could not be found. 083 */ 084 public SourceNameResolver(String target) throws SourceNotFoundException 085 { 086 this(); 087 lookup(target); 088 } 089 090 /** Does a circular increment of CURRENT_RESOURCE */ 091 private void incrementCurrentResourceCounter() 092 { 093 CURRENT_RESOURCE = ((CURRENT_RESOURCE + 1) < serviceUrls.length)? CURRENT_RESOURCE + 1 : 0; 094 } 095 096 /** 097 * @return a list of errors found trying to process our request. 098 */ 099 public List<String> getErrors() 100 { 101 List<String> errs = new ArrayList<String>(); 102 103 List<Target> targets = this.data.getTarget(); 104 if (targets != null) 105 { 106 for (Target t : targets) 107 { 108 List<String> err = t.getERROR(); 109 if (err != null) 110 errs.addAll(err); 111 112 List<String> info = t.getINFO(); 113 if (info != null) 114 for (String i : info) 115 if (i.startsWith("!***") || i.startsWith("***")) 116 errs.add(i); 117 } 118 } 119 120 return errs; 121 } 122 123 //XXX temporary! 124 public Sesame getData() 125 { 126 return this.data; 127 } 128 129 //TODO Method comments 130 public Sesame lookup(String target) throws SourceNotFoundException 131 { 132 //Try all addresses if we have to, but then give up. 133 for (int tries = 0; tries < serviceUrls.length; tries++) 134 { 135 try 136 { 137 //make the call, passing in the arguments as an array of objects. Note 138 //that this cast is safe because I explicitly set the return type 139 //to string. 140 //Note on arguments: first arg is the target name, the second tells 141 //Sesame to return xml, the 3rd to return all aliases of the target, 142 //and the 4th tells it to query Simbad, and then, if not found in 143 //Simbad, query NED. 144 String xml = (String)call.invoke(new Object[] {target, "x", true, "SN"}); 145 146 log.debug("Returned XML:\n" + xml); 147 148 //Create a Sesame object from the xml 149 JaxbUtility xmlUtil = new JaxbUtility(); 150 xmlUtil.setFailIfDefaultSchemaNotFound(false); 151 xmlUtil.setLookForDefaultSchema(false); 152 this.data = xmlUtil.readObjectAsXmlFrom(new StringReader(xml), Sesame.class, null); 153 154 List<String> errs = getErrors(); 155 156 //if this worked and we haven't thrown an exception, we can break 157 //out of the loop. 158 if (errs == null || errs.isEmpty()) 159 break; 160 161 else 162 { 163 StringBuilder errors = new StringBuilder("Errors found while attempting to resolve '"); 164 errors.append(target); 165 errors.append("' @ "); 166 errors.append(serviceUrls[CURRENT_RESOURCE]); 167 errors.append(":\n\t"); 168 169 for (String err : errs) 170 { 171 errors.append(err); 172 errors.append("\n\t"); 173 } 174 175 log.warn(errors.toString()); 176 incrementCurrentResourceCounter(); 177 } 178 } 179 180 //Service is broken, try a different one. 181 catch (RemoteException re) 182 { 183 log.warn("Axis service is down @ " + serviceUrls[CURRENT_RESOURCE], re); 184 185 //increment our place in our round robin attempts to find a working service. 186 incrementCurrentResourceCounter(); 187 188 log.warn("Trying next service @ " + serviceUrls[CURRENT_RESOURCE]); 189 } 190 191 //Any other exception we'll just have to barf on and assume we can't 192 //get the source because something else is wrong. 193 catch (Exception e) 194 { 195 throw new SourceNotFoundException(e); 196 } 197 } 198 199 200 // Only pay attention to the first target as I don't think we can return 201 // more than one target with the way we're using the call. 202 if (this.data != null) 203 { 204 List<Target> targets = this.data.getTarget(); 205 if (targets != null && !targets.isEmpty()) 206 { 207 List<Resolver> resolvers = targets.get(0).getResolver(); 208 if (resolvers != null) 209 { 210 for (Resolver r : resolvers) 211 { 212 // If there's a position, then return our data, we found it! 213 if (hasPosition(r)) 214 return this.data; 215 } 216 } 217 } 218 } 219 220 //If we didn't get any position information back, there was an error 221 //so we throw a SourceNotFoundException. 222 throw new SourceNotFoundException("Could not find source: " + target); 223 } 224 225 private boolean hasPosition(Resolver r) 226 { 227 if (r != null) 228 { 229 List<JAXBElement<?>> info = r.getINFOOrERROROrOtype(); 230 231 if (info != null) 232 { 233 for (JAXBElement<?> e : info) 234 { 235 String name = e.getName().getLocalPart(); 236 if ("jradeg".equalsIgnoreCase(name)) 237 return true; 238 } 239 } 240 } 241 242 return false; 243 } 244 }