001package votorola.a.trust; // Copyright 2008-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.sql.*; 005import javax.script.*; 006import javax.xml.stream.*; 007import votorola.a.*; 008import votorola.a.voter.*; 009import votorola.g.*; 010import votorola.g.lang.*; 011import votorola.g.hold.*; 012import votorola.g.io.*; 013import votorola.g.logging.*; 014import votorola.g.sql.*; 015import votorola.g.xml.stream.*; 016 017 018/** The path to a snap/readyTrace record. It is the file part of the backing for a traced 019 * trust network and a compiled registration list. It is guaranteed to be in canonical 020 * form at the time of construction. 021 * 022 * @see <a href='../../../../../s/manual.xht#line-votrace'>snap/readyTrace record</a> 023 * @see NetworkTrace 024 */ 025public @ThreadSafe final class ReadyDirectory extends OutputStore.ReadyDirectory 026{ 027 028 // cf. a/count/ReadyDirectory 029 030 031 private static final long serialVersionUID = 1L; 032 033 034 035 /** Creates a ready directory and returns an instance of it. 036 * 037 * @param snapDirectory the parent directory in which to create the new ready 038 * directory. 039 */ 040 public static ReadyDirectory createReadyDirectory( final File snapDirectory ) throws IOException 041 { 042 final File d = OutputStore.mkdirsS( snapDirectory, "readyTrace" ); 043 // no admin to store yet, so that's it 044 return new ReadyDirectory( d.getPath() ); 045 } 046 047 048 049 /** Constructs a ReadyDirectory from an abstract (File) pathname. 050 * 051 * @param d the abstract pathname. It will be converted to canonical form if 052 * necessary. 053 * 054 * @throws FileNotFoundException if no directory exists at the specified pathname. 055 * @see #createReadyDirectory(File) 056 */ 057 public ReadyDirectory( File d ) throws IOException { super( d ); } 058 059 060 061 /** Constructs a ReadyDirectory from a string pathname. 062 * 063 * @param pathname the string pathname, per {@linkplain File#File(String) 064 * File}(pathname). It will be converted to canonical form if necessary. 065 * 066 * @throws FileNotFoundException if no directory exists at the specified pathname. 067 * @see #createReadyDirectory(File) 068 */ 069 public ReadyDirectory( String pathname ) throws IOException { super( new File( pathname )); } 070 071 072 073 // ------------------------------------------------------------------------------------ 074 075 076 /** The file storing the snapshot of registrant input from the streetwiki, in XML 077 * format. 078 */ 079 File inRegistrationFile() { return inRegistrationFile; } 080 081 082 private final File inRegistrationFile = new File( snapDirectory(), "_in_registration.xml" ); 083 084 085 086 /** Traces the trust network, compiles the registration list, and posts the results to 087 * the database and filebase. 088 * 089 * @see #isMounted() 090 * @see #unmount(VoteServer.Run) 091 */ 092 public NetworkTrace mount( final Trustserver trustserver, final boolean isVerbose ) 093 throws javax.mail.internet.AddressException, IOException, ScriptException, SQLException, 094 XMLStreamException 095 { 096 final Database database = trustserver.vsRun().database(); 097 final Membership.Table membershipTable = new Membership.Table( ReadyDirectory.this, database ); 098 membershipTable.drop(); 099 membershipTable.create(); 100 final TraceNodeW.Table traceNodeTable = new TraceNodeW.Table( ReadyDirectory.this, database ); 101 traceNodeTable.drop(); 102 traceNodeTable.create(); 103 final TrustEdge.Table trustEdgeTable = new TrustEdge.Table( ReadyDirectory.this, database ); 104 trustEdgeTable.drop(); 105 trustEdgeTable.create(); 106 synchronized( database ){ database.connection().setAutoCommit( false ); } // bulk write 107 try 108 { 109 110 // 1. Read snapshot of registrant input, create trace nodes, divisional memberships 111 // and trust edges 112 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 113 if( isVerbose ) System.out.print( "1" ); 114 // Pre-creation of edges records the skeletal structure of the trust network in relational 115 // form, so it need not be repeated in each trace node. Pre-creation of trace nodes 116 // allows for subsequent lookup of edge sides (source and destination node) in pass 2, so 117 // the edge bars can be recorded there, prior to attempting the extension of edges. 118 // (While we're at it, we pre-create the memberships too.) 119 { 120 final InputStream in = new BufferedInputStream( new FileInputStream( inRegistrationFile )); 121 try 122 { 123 final XMLStreamReader r; 124 synchronized( XMLInputFactoryX.class ) 125 { 126 r = XMLInputFactoryX.SIMPLE_INPUT_FACTORY.createXMLStreamReader( 127 /*systemId*/inRegistrationFile.toString(), in ); 128 } 129 try 130 { 131 while( r.hasNext() ) 132 { 133 r.next(); 134 if( r.isStartElement() && "in".equals( r.getLocalName() )) 135 { 136 final Registration reg = new Registration( r ); 137 final IDPair registrant = reg.registrant(); 138 for( final String div: reg.divisions() ) 139 { 140 final Membership membership = new Membership( registrant, div ); 141 membership.write( membershipTable ); 142 } 143 final TraceNodeW node = new TraceNodeW( registrant, reg.getArea(), 144 reg.getCountryCode(), reg.getGeohandle(), reg.getOtherProperties() ); 145 node.write( traceNodeTable ); 146 for( final IDPair registrant0: reg.trusters() ) 147 { 148 final TrustEdge edge = new TrustEdge( registrant0, registrant ); 149 edge.write( trustEdgeTable ); 150 } 151 } 152 } 153 } 154 finally{ r.close(); } 155 } 156 finally{ in.close(); } 157 } 158 159 // 2. For each trust edge, record any bar 160 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 161 if( isVerbose ) System.out.print( "2" ); 162 trustEdgeTable.init_bars( trustserver, traceNodeTable ); 163 164 // 3. Inject primary edges into the trust network and throughout 165 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 166 if( isVerbose ) System.out.print( "3" ); 167 final NetworkTracer tracer = new NetworkTracer( traceNodeTable, trustEdgeTable ); 168 for( TrustEdge.Primary primary: trustserver.primaryTrustList() ) 169 { 170 LoggerX.i(getClass()).config( "injecting primary trust edge(s) to registant " + primary.registrant1() ); 171 final TraceNodeW primaryDestination = traceNodeTable.get( primary.registrant1() ); 172 if( primaryDestination == null ) 173 { 174 LoggerX.i(getClass()).warning( "cannot extend primary trust edge(s) to " + primary.registrant1() + ": no such registrant" ); 175 continue; 176 } 177 178 // assert primaryDestination.trustLevel() == 0; 179 /// why? an earlier primary could have trusted it 180 for( int e = 0, eN = primary.edgeCount(); e < eN; ++e ) 181 { 182 primaryDestination.attachTrustEdge( Integer.MAX_VALUE ); 183 } 184 primaryDestination.write( traceNodeTable ); 185 tracer.extendLevelChange( primaryDestination, 0 ); // throughout network 186 } 187 188 // 4. Serialize the mounted trace into registration lists 189 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 190 if( isVerbose ) System.out.print( "4" ); 191 final class NodeRunner implements TraceNodeW.Runner 192 { 193 private String area = ""; // initially not a valid area, and not null 194 195 Spool spool = Spool0.i(); 196 197 private BufferedWriter w = null; 198 199 public void run( final TraceNodeW node ) 200 { 201 try 202 { 203 if( !ObjectX.nullEquals( node.getArea(), area )) 204 { 205 spool.unwind(); 206 spool = new Spool1(); // if not unwound above, then farther below 207 area = node.getArea(); 208 209 final File traceFile = newListSerialFile( area ); 210 final BufferedWriter writerNew = new BufferedWriter( 211 new OutputStreamWriter( new FileOutputStream( traceFile ), "UTF-8" )); 212 spool.add( new Hold() 213 { 214 public void release() 215 { 216 try 217 { 218 writerNew.append( "\t</register>\n" ); 219 writerNew.close(); 220 } 221 catch( IOException x ) { throw new VotorolaRuntimeException( x ); } 222 } 223 }); 224 w = writerNew; 225 w.append( "<?xml version='1.0' encoding='UTF-8'?> <!-- -*-coding: utf-8;-*- -->\n" ); 226 w.append( "<register" ); 227 if( area != null ) w.append( " area='" + area + "'" ); 228 229 w.append( ">\n" ); 230 } 231 232 node.toXML( membershipTable.divisionSet(node.registrant().email()), w ); 233 } 234 catch( IOException|SQLException x ) { throw new VotorolaRuntimeException( x ); } 235 } 236 }; 237 238 mountedDirectory.mkdir(); 239 final NodeRunner nodeRunner = new NodeRunner(); 240 traceNodeTable.run( nodeRunner ); 241 nodeRunner.spool.unwind(); 242 } 243 finally{ synchronized( database ){ database.connection().setAutoCommit( true ); }} 244 // will do a commit, too 245 246 // - - - 247 final NetworkTrace trace = new NetworkTrace( ReadyDirectory.this ); 248 trace.writeObjectToSerialFile(); 249 if( isVerbose ) System.out.println(); 250 return trace; 251 } 252 253 254 /** The directory to which the trace is serialized, when mounted. 255 */ 256 public File mountedDirectory() { return mountedDirectory; } 257 258 259 private final File mountedDirectory = new File( ReadyDirectory.this, "_mountedTrace" ); 260 261 262 263 /** Constructs the file to which a registration list is serialized when mounted. Each 264 * {@linkplain TraceNode#getArea() area} has a separate list, and the list of 265 * unlocalized registrations is serialized to the file '_mountedTrace/_default.xml'. 266 */ 267 public File newListSerialFile( final String area ) 268 { 269 return new File( mountedDirectory(), (area == null? "_default": area) + ".xml" ); 270 } 271 272 273 274 /** Constructs the file to which a network trace is serialized when mounted. 275 */ 276 public File newTraceSerialFile() 277 { 278 return new File( mountedDirectory(), "_trace.serial" ); 279 } 280 281 282 283 /** A file filter that accepts only apparent ready directories. 284 */ 285 public static final FileFilter READY_DIRECTORY_FILTER = new FileFilter() 286 { 287 public boolean accept( final File file ) 288 { 289 final String name = file.getName(); 290 return file.isDirectory() && name.startsWith( "readyTrace-" ) 291 && OutputStore.isS( OutputStore.suffix( name )); 292 } 293 }; 294 295 296 297 /** Reverses a previous mount, erasing the results from the database and filebase. 298 * 299 * @return true if the trace was unmounted, false if it was not mounted to begin 300 * with, either in whole or part. 301 * 302 * @see #isMounted() 303 * @see #mount(Trustserver,boolean) 304 */ 305 public boolean unmount( final VoteServer.Run vsRun ) throws IOException, SQLException 306 { 307 boolean unmounted = false; // thus far 308 if( mountedDirectory.isDirectory() ) 309 { 310 if( !FileX.deleteRecursive( mountedDirectory )) throw new IOException( "unable to recursively delete directory: " + mountedDirectory ); 311 312 unmounted = true; 313 } 314 final Database database = vsRun.database(); 315 if( new Membership.Table(ReadyDirectory.this,database).drop() ) unmounted = true; 316 if( new TraceNodeW.Table(ReadyDirectory.this,database).drop() ) unmounted = true; 317 if( new TrustEdge.Table(ReadyDirectory.this,database).drop() ) unmounted = true; 318 return unmounted; 319 } 320 321 322 323 // - O u t p u t - S t o r e . R e a d y - D i r e c t o r y -------------------------- 324 325 326 /** Answers whether the trace is nominally mounted. 327 */ 328 public boolean isMounted() { return mountedDirectory.isDirectory(); } 329 330 331 332}