001package votorola.a.trust; // Copyright 2010-2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import java.io.*; 004import java.util.*; 005import java.util.concurrent.locks.*; 006import java.util.logging.*; 007import java.sql.*; 008import javax.mail.internet.*; 009import javax.script.*; 010import votorola.a.*; 011import votorola.a.response.*; 012import votorola.a.voter.*; 013import votorola.g.lang.*; 014import votorola.g.logging.*; 015import votorola.g.script.*; 016import votorola.g.sql.*; 017import votorola.g.util.*; 018 019 020/** A trust server. 021 * 022 * @see <a href='http://reluk.ca/w/Stuff:Trustserver' 023 * >Stuff:Trustserver</a> 024 */ 025@ThreadRestricted("holds lock()") public final class Trustserver extends VoterService 026{ 027 028 // cf. a/count/PollService 029 030 031 /** Constructs a Trustserver. 032 * 033 * @param s the compiled startup configuration script. 034 */ 035 public static @ThreadSafe Trustserver newTrustserver( final VoteServer.Run run, 036 final JavaScriptIncluder s ) throws IOException, ScriptException, SQLException 037 { 038 final ConstructionContext cc = new ConstructionContext( run.voteServer(), s ); 039 s.invokeKnownFunction( "constructingTrustserver", cc ); 040 return new Trustserver( run, cc ); 041 } 042 043 044 045 private Trustserver( final VoteServer.Run run, final ConstructionContext cc ) 046 throws IOException, ScriptException, SQLException 047 { 048 super( run, cc ); 049 init( new ArrayList<CommandResponder>() ); 050 constructionContext = null; // done with it, free the memory 051 } 052 053 054 055 private ConstructionContext cc() { return (ConstructionContext)constructionContext; } // nulled after init 056 057 058 059 // ```````````````````````````````````````````````````````````````````````````````````` 060 // init for early use 061 062 063 private final File startupConfigurationFile = cc().startupConfigurationFile(); 064 065 066 067 // ------------------------------------------------------------------------------------ 068 069 070 /** The trustserver's facility for converting street addresses to cartographic 071 * coordinates. 072 */ 073 public @ThreadSafe GoogleGeocoder geocoder() { return geocoder; } 074 075 076 private final GoogleGeocoder geocoder = new GoogleGeocoder( vsRun.geocodeTable() ); 077 078 079 080 /** Retrieves a registrant's node from a compiled network trace. This is a 081 * convenience method. 082 * 083 * @param trace the trace to use. This may be null, causing lookup the currently 084 * {@linkplain #traceToReport() reported trace}. Note that lookup involves 085 * locking overhead in threaded runs. 086 * 087 * @return TraceNodeW or TraceNodeIC per {@linkplain 088 * TraceNodeW.Table#getOrCreate(IDPair) getOrCreate}(registrant). Or, if 089 * lookup of the trace itself fails, a TraceNode0 with default values. 090 */ 091 public @ThreadSafe TraceNode getTraceNode( NetworkTrace trace, final IDPair registrant ) 092 throws IOException, SQLException 093 { 094 if( trace == null ) trace = traceToReportT(); 095 return trace == null? new TraceNode0( registrant ): 096 trace.traceNodeTable().getOrCreate( registrant ); 097 } 098 099 100 101 /** The primary trust to extend for each trace of the trust network. 102 * 103 * @return unmodifiable list of primary trust extensions. 104 * 105 * @see ConstructionContext#addPrimaryTrust(IDPair,int) 106 */ 107 List<TrustEdge.Primary> primaryTrustList() { return primaryTrustList; } 108 109 110 private final ArrayListU<TrustEdge.Primary> primaryTrustList = 111 new ArrayListU<TrustEdge.Primary>( cc().getPrimaryTrustArray() ); 112 113 114 115 /** The compiled runtime-configuration script for the trustserver. Its source file is 116 * located at: 117 * 118 * <p class='indent'>~/{@linkplain VoteServer#votorolaDirectory() 119 * votorola}/trust/trustserver-run.js</p> 120 * 121 * <p>The language is JavaScript. There are restrictions on the {@linkplain 122 * votorola.g.script.JavaScriptIncluder character encoding}.</p> 123 * 124 * @see <a href='../../../../../a/trust/trustserver-run.js' 125 * >trustserver-run.js (example script)</a> 126 * @see <a href='../../../../../s/manual.xht#trustserver-run.js' 127 * >../manual.xht#trustserver-run.js (FIX broken docs)</a> 128 */ 129 @Warning( "thread restricted object" ) public JavaScriptIncluder runtimeConfigurationScript() 130 { 131 assert lock.isHeldByCurrentThread(); // this method is safe, but not the object 132 return runtimeConfigurationScript; 133 } 134 135 136 private final JavaScriptIncluder runtimeConfigurationScript = new JavaScriptIncluder( 137 new File( serviceDirectory(), "trustserver-run.js" )); 138 139 140 141 /** The current network trace to report, or null if there is none. 142 * 143 * @see votorola.a.trust.NetworkTrace.VoteServerScope#readyToReportLink() 144 */ 145 public NetworkTrace traceToReport() throws IOException, SQLException 146 { 147 assert lock.isHeldByCurrentThread(); 148 final File readyToReportLink = vsRun.voteServer().scopeTrace().readyToReportLink(); 149 ReadyDirectory readyDirectory = null; // so far 150 if( readyToReportLink.exists() ) 151 { 152 readyDirectory = new ReadyDirectory( readyToReportLink.getCanonicalPath() ); 153 } 154 if( readyDirectory == null ) 155 { 156 if( traceToReport != null ) 157 { 158 logger.info( readyToReportLink + ": link is lost, stopping report: " + traceToReport.readyDirectory() ); 159 traceToReport = null; 160 } 161 return traceToReport; 162 } 163 164 if( !readyDirectory.isMounted() ) 165 { 166 logger.warning( readyToReportLink + ": trace not mounted: " + readyDirectory ); 167 traceToReport = null; 168 return traceToReport; 169 } 170 171 if( traceToReport == null || !traceToReport.isObjectReadFromSerialFile( readyDirectory )) 172 { 173 logger.info( "starting new trace report: " + readyDirectory ); 174 traceToReport = NetworkTrace.readObjectFromSerialFile( readyDirectory ); 175 final Database d = vsRun.database(); 176 traceToReport.init( new Membership.Table( readyDirectory, d ), 177 new TraceNodeW.Table( readyDirectory, d )); 178 } 179 return traceToReport; 180 } 181 182 183 private NetworkTrace traceToReport; // lazily set/reset through traceToReport() 184 185 186 187 /** The current network trace to report, or null if there is none. This is just a 188 * thread safe wrapper that automatically siezes and releases the lock. 189 */ 190 public @ThreadSafe NetworkTrace traceToReportT() throws IOException, SQLException 191 { 192 lock.lock(); 193 try { return traceToReport(); } 194 finally { lock.unlock(); } 195 } 196 197 198 199 // - V o t e r - S e r v i c e -------------------------------------------------------- 200 201 202 /** @see <a href='../../../../../s/manual.xht#trustserver.js' 203 * >../manual.xht#trustserver.js (FIX broken docs)</a> 204 */ 205 public @ThreadSafe @Override File startupConfigurationFile() 206 { 207 return startupConfigurationFile; 208 } 209 210 211 212 /** @see ConstructionContext#setSummaryDescription(String) 213 */ 214 public @ThreadSafe @Override String summaryDescription() { return summaryDescription; } 215 216 217 private final String summaryDescription = cc().getSummaryDescription(); 218 219 220 221 /** @see ConstructionContext#setTitle(String) 222 */ 223 public @ThreadSafe @Override String title() { return title; } 224 225 226 private final String title = cc().getTitle(); 227 228 229 230 // ==================================================================================== 231 232 233 /** A context for configuring the construction of a {@linkplain Trustserver 234 * Trustserver}. Each construction is configured by the trusterver's {@linkplain 235 * Trustserver#startupConfigurationFile startup configuration script} (s). During 236 * construction, an instance of this context (tCC) is passed to s, via 237 * s::constructingTrustserver(tCC). 238 */ 239 public static @ThreadRestricted("constructor") final class ConstructionContext 240 extends VoterService.ConstructionContext 241 { 242 243 244 private ConstructionContext( VoteServer _voteServer, final JavaScriptIncluder s ) 245 { 246 super( "trustserver", s ); 247 voteServer = _voteServer; 248 249 setSummaryDescription( "This is the trustserver. " 250 + "Further information is unavailable because the 'constructingTrustserver' function " 251 + "of script " + startupConfigurationFile() + " " 252 + "makes no call to 'setSummaryDescription'." ); 253 } 254 255 256 257 // -------------------------------------------------------------------------------- 258 259 260 /** The primary trust to extend for each trace of the trust network. 261 * 262 * @see #addPrimaryTrust(IDPair,int) 263 */ 264 public TrustEdge.Primary[] getPrimaryTrustArray() 265 { 266 return primaryTrustList.toArray( new TrustEdge.Primary[primaryTrustList.size()] ); 267 } 268 269 270 private final ArrayList<TrustEdge.Primary> primaryTrustList 271 = new ArrayList<TrustEdge.Primary>(); 272 273 274 /** Adds primary trust extensions. By default, there are no primary trust 275 * extensions and therefore no trust at all in compiled networks. 276 * 277 * @see #getPrimaryTrustArray() 278 */ 279 public void addPrimaryTrust( final IDPair registrant1, final int edgeCount ) 280 { 281 primaryTrustList.add( new TrustEdge.Primary( registrant1, edgeCount )); 282 } 283 284 285 /** Adds primary trust extensions for the specified registrant. This is a 286 * convenience method. 287 * 288 * @see #addPrimaryTrust(IDPair,int) 289 */ 290 public void addPrimaryTrustForUsername( final String registrant1Username, 291 final int edgeCount ) throws AddressException 292 { 293 addPrimaryTrust( IDPair.fromUsername(registrant1Username), edgeCount ); 294 } 295 296 297 298 /** @see Trustserver#summaryDescription() 299 * @see #setSummaryDescription(String) 300 */ 301 public String getSummaryDescription() { return summaryDescription; } 302 303 304 private String summaryDescription; 305 306 307 /** Sets the summary description of the trustserver. The default value is a 308 * placeholder with configuration instructions for the administrator. 309 * 310 * @throws IllegalArgumentException if the description contains any 311 * newline characters, because they might render inconsistently across 312 * different types of user interface. 313 * @see Trustserver#summaryDescription() 314 */ 315 public void setSummaryDescription( String summaryDescription ) 316 { 317 if( summaryDescription.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" ); 318 319 this.summaryDescription = summaryDescription; 320 } 321 322 323 324 /** @see Trustserver#title() 325 * @see #setTitle(String) 326 */ 327 public String getTitle() { return title; } 328 329 330 private String title = "Trustserver"; 331 332 333 /** Sets the title of the trustserver. The default value is "Trustserver". 334 * 335 * @see Trustserver#title() 336 */ 337 public void setTitle( String title ) { this.title = title; } 338 339 340 341 /** The vote-server. 342 */ 343 public VoteServer voteServer() { return voteServer; } 344 345 346 private final VoteServer voteServer; 347 348 349 } 350 351 352 353//// P r i v a t e /////////////////////////////////////////////////////////////////////// 354 355 356 private static final Logger logger = LoggerX.i( Trustserver.class ); 357 358 359 360}