();
/** @see PollService#displayTitle()
* @see #setDisplayTitle(String)
*/
public String getDisplayTitle() { return displayTitle; }
private String displayTitle;
/** Sets the display title of the poll.
*
* @see PollService#displayTitle()
*/
public void setDisplayTitle( String _displayTitle ) { displayTitle = _displayTitle; }
/** @see PollService#divisionPageName()
* @see #setDivisionPageName(String)
*/
public String getDivisionPageName() { return divisionPageName; }
private String divisionPageName;
/** Sets the pollwiki pagename of the polling division. The default value is
* null, meaning unspecified.
*
* @see PollService#divisionPageName()
*/
public void setDivisionPageName( String _divisionPageName )
{
divisionPageName = _divisionPageName;
}
/** @see PollService#divisionSmallMapPageName()
* @see #setDivisionSmallMapPageName(String)
*/
public String getDivisionSmallMapPageName() { return divisionSmallMapPageName; }
private String divisionSmallMapPageName;
/** Sets the pollwiki pagename of the polling divisionSmallMap. The default value is
* null, meaning unspecified.
*
* @see PollService#divisionSmallMapPageName()
*/
public void setDivisionSmallMapPageName( String _divisionSmallMapPageName )
{
divisionSmallMapPageName = _divisionSmallMapPageName;
}
/** @see PollService#issueType()
* @see #setIssueType(String)
*/
public String getIssueType() { return issueType; }
private String issueType = "Issue";
/** Sets the issue type of the poll. The default value is "Issue", meaning
* unspecified.
*
* @see PollService#issueType()
*/
public void setIssueType( String _issueType )
{
if( _issueType == null ) throw new NullPointerException();
issueType = _issueType;
}
/** @see PollService#populationSize()
* @see #setPopulationSize(long)
*/
public long getPopulationSize() { return populationSize; }
private long populationSize; // default zero = unknown, so unlikely to divide by it
/** Sets the population base of this poll. The default value is zero,
* meaning unknown.
*
* @see PollService#populationSize()
*/
public void setPopulationSize( long _populationSize )
{
populationSize = _populationSize;
}
/** @see PollService#populationSizeExplanation()
* @see #setPopulationSizeExplanation(String)
*/
public String getPopulationSizeExplanation() { return populationSizeExplanation; }
private String populationSizeExplanation = "Estimated number of eligible voters.";
/** Sets the explanation of the population base. The default value is
* "Estimated number of eligible voters.".
*
* @see PollService#populationSizeExplanation()
*/
public void setPopulationSizeExplanation( String _populationSizeExplanation )
{
populationSizeExplanation = _populationSizeExplanation;
}
/** The vote-server.
*/
public VoteServer voteServer() { return voteServer; }
private final VoteServer voteServer;
/** @see PollService#summaryDescription()
* @see #setSummaryDescription(String)
*/
public String getSummaryDescription() { return summaryDescription; }
private String summaryDescription;
/** Sets the summaryDescription of the poll. The default value is a
* placeholder with configuration instructions for the administrator.
*
* @see PollService#summaryDescription()
*/
public void setSummaryDescription( String _summaryDescription )
{
summaryDescription = _summaryDescription;
}
/** @see PollService#wgLogoImageLocation()
* @see #setWGLogoImageLocation(String)
*/
public String getWGLogoImageLocation() { return wgLogoImageLocation; }
private String wgLogoImageLocation; // no easy way to obtain local URL of default image, till the web interface is running
/** Sets the image location for the wiki logo. The default value is
* null, meaning unspecified.
*
* @see PollService#wgLogoImageLocation()
*/
public void setWGLogoImageLocation( String _wgLogoImageLocation )
{
wgLogoImageLocation = _wgLogoImageLocation;
}
/** @see PollService#wgLogoLinkTarget()
* @see #setWGLogoLinkTarget(String)
*/
public String getWGLogoLinkTarget() { return wgLogoLinkTarget; }
private String wgLogoLinkTarget;
/** Sets the link target of the wiki logo. The default value is the
* {@linkplain PollwikiVS#uri() base URI} of the wiki.
*
* @see PollService#wgLogoLinkTarget()
*/
public void setWGLogoLinkTarget( String _wgLogoLinkTarget )
{
wgLogoLinkTarget = _wgLogoLinkTarget;
}
/** Answers whether scratch construction is being attempted, in which cached
* configuration items are ignored. During scratch construction, scripts are
* expected to report verbose progress (via 'print' commands) for the benefit of
* the administrator or user who requested it.
*/
public boolean isReconstruct() { return isReconstruct; }
private final boolean isReconstruct;
}
// ====================================================================================
/** API for all polls within the scope of a vote-server.
*
* @see VoteServer#scopePoll()
*/
public static @ThreadSafe final class VoteServerScope
{
/** Constructs a VoteServerScope.
*/
public @Warning( "non-API" ) VoteServerScope( VoteServer _voteServer,
final VoteServer.ConstructionContext vsCC )
{
voteServer = _voteServer;
pollCacheCapacity = vsCC.getPollCacheCapacity();
configurationFile = new File( voteServer.votorolaDirectory(), "poll-service.js" );
}
// --------------------------------------------------------------------------------
/** The configuration file for all polls. It is located at:
*
* {@linkplain VoteServer#votorolaDirectory()
* votorolaDirectory}/poll-service.js
*
* The language is JavaScript. There are restrictions on the {@linkplain
* votorola.g.script.JavaScriptIncluder character encoding}.
*
* @see PollService#configurationScript()
* @see poll-service.js (example script)
* @see ../manual.xht#poll-service.js
*/
File configurationFile() { return configurationFile; }
private final File configurationFile;
/** The maximum number of entries in the poll cache. The cache uses an LRU
* algorithm, discarding "least recently used" entries to stay within this
* capacity.
*
* @see votorola.a.VoteServer.ConstructionContext#setPollCacheCapacity(int)
*/
final int pollCacheCapacity() { return pollCacheCapacity; }
private final int pollCacheCapacity;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
/** API for all polls within the scope of a vote-server run.
*
* @see votorola.a.VoteServer.Run#scopePoll()
*/
public @ThreadSafe final class Run
{
/** Constructs a Run.
*/
public @Warning( "non-API" ) Run( final VoteServer.Run _vsRun )
{
vsRun = _vsRun;
wikiCacheLastChurnTimeA = new AtomicLong( // 1a before 1b
vsRun.voteServer().pollwiki().cache().lastChurnTime() );
final float loadFactor = 0.75f;
final int spareCapacity = 2;
pollCache = new LinkedHashMap( // 1b
/*initial capacity*/(int)( (pollCacheCapacity + spareCapacity) / loadFactor ),
loadFactor, /*accessOrder, for LRU cache*/true )
{
protected boolean removeEldestEntry( Map.Entry eldest )
{
return size() > pollCacheCapacity;
}
};
}
// ----------------------------------------------------------------------------
/** Returns a newly constructed poll, caching it for later retrieval.
*
* @param logWriter the writer for logging construction messages. When
* non-null, {@linkplain ConstructionContext#isReconstruct() scratch
* construction} is attempted. (The writer must be a PrintWriter for
* sake of scripts, where conventional 'print' statements fail with
* other writer types.)
*/
public PollService constructCachedPoll( final String name, final PrintWriter logWriter )
throws IOException, ScriptException, SQLException
{
final boolean isReconstruct = logWriter != null;
logger.fine(( isReconstruct? "constructing poll from scratch: ":"constructing poll: ") + name );
final JavaScriptIncluder s = new JavaScriptIncluder( configurationFile );
final ScriptContext sContext = s.engine().getContext();
final Writer oldWriter; final Writer oldErrorWriter;
if( isReconstruct )
{
oldWriter = sContext.getWriter();
sContext.setWriter( logWriter );
oldErrorWriter = sContext.getErrorWriter();
sContext.setErrorWriter( logWriter );
}
else oldWriter = oldErrorWriter = null;
final ConstructionContext cc = new ConstructionContext( voteServer, name, s,
isReconstruct );
s.invokeKnownFunction( "constructingPoll", cc );
final PollService poll = new PollService( vsRun, s, cc );
if( isReconstruct ) // restore old writers
{
sContext.setWriter( oldWriter );
sContext.setErrorWriter( oldErrorWriter );
}
synchronized( Run.this ) { pollCache.put( name, poll ); }
s.invokeKnownFunction( "pollConstructed", poll );
return poll;
}
/** Returns a poll, if necessary constructing and caching it for later
* retrieval. Note that the poll cache will be automatically cleared if it
* is detected that the wiki cache has been churned.
*
* @see WikiCache#lastChurnTime()
* @see votorola.a.count.PollService.VoteServerScope#pollCacheCapacity()
*/
public PollService ensurePoll( final String name )
throws IOException, ScriptException, SQLException
{
if( name == null ) throw new NullPointerException();
PollService poll;
{
final long timeIs = vsRun.voteServer().pollwiki().cache().lastChurnTime();
final long timeWas = wikiCacheLastChurnTimeA.getAndSet( timeIs );
// If this detector can result redundant clearances, in some cases,
// it can never result in a permanently skipped one.
if( timeWas == timeIs ) synchronized( Run.this )
{
poll = pollCache.get( name );
}
else
{
logger.config( "clearing poll cache, as apparently wiki cache was churned" );
pollCache.clear();
poll = null;
}
}
if( poll == null ) poll = constructCachedPoll( name, /*logWriter*/null );
// Non-atomic test/construction here. The construction of the poll may
// therefore be duplicated if the same poll is requested by multiple
// threads. This should not happen too often, and it will do no harm.
// Freeing the lock during poll construction and config (very slow) is
// more important.
return poll;
}
/** The vote-server run.
*/
public VoteServer.Run vsRun() { return vsRun; }
private final VoteServer.Run vsRun;
//// P r i v a t e ///////////////////////////////////////////////////////////////
private final AtomicLong wikiCacheLastChurnTimeA;
private @ThreadRestricted("holds Run.this") final HashMap pollCache;
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////
private final VoteServer voteServer;
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
private static final Logger logger = LoggerX.i( PollService.class );
}