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}