// yacyVersion.java
// ----------------
// (C) 2007 by Michael Peter Christen; mc@anomic.de, Frankfurt a. M., Germany
// first published 27.04.2007 on http://yacy.net
//
// This is a part of YaCy, a peer-to-peer based web search engine
//
// $LastChangedDate$
// $LastChangedRevision$
// $LastChangedBy$
//
// LICENSE
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package de.anomic.yacy ;
import java.io.File ;
import java.io.IOException ;
import java.net.MalformedURLException ;
import java.util.ArrayList ;
import java.util.Comparator ;
import java.util.HashMap ;
import java.util.Iterator ;
import java.util.Map ;
import java.util.TreeSet ;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import de.anomic.htmlFilter.htmlFilterContentScraper ;
import de.anomic.http.httpc ;
import de.anomic.plasma.plasmaSwitchboard ;
import de.anomic.server.serverCore ;
import de.anomic.server.serverSystem ;
import de.anomic.server.logging.serverLog ;
public final class yacyVersion implements Comparator , Comparable {
// general release info
public static final float YACY_SUPPORTS_PORT_FORWARDING = ( float ) 0.383 ;
public static final float YACY_SUPPORTS_GZIP_POST_REQUESTS = ( float ) 0.40300772 ;
public static final float YACY_ACCEPTS_RANKING_TRANSMISSION = ( float ) 0.414 ;
public static final float YACY_HANDLES_COLLECTION_INDEX = ( float ) 0.486 ;
public static final float YACY_POVIDES_REMOTECRAWL_LISTS = ( float ) 0.550 ;
public static final float YACY_STANDARDREL_IS_PRO = ( float ) 0.557 ;
// information about latest release, retrieved by other peers release version
public static double latestRelease = 0.1 ; // this value is overwritten when a peer with later version appears
// information about latest release, retrieved from download pages
// this static information should be overwritten by network-specific locations
// for details see yacy.network.unit
private static HashMap /* URL:TreeSet[]*/ latestReleases = new HashMap ( ) ;
public static ArrayList latestReleaseLocations /*string*/ = new ArrayList ( ) ; // will be initialized with value in yacy.network.unit
// private static release info about this release; is generated only once and can be retrieved by thisVersion()
private static yacyVersion thisVersion = null ;
// class variables
public float releaseNr ;
public String dateStamp ;
public int svn ;
public boolean fullRelease , mainRelease ;
public yacyURL url ;
public String name ;
public yacyVersion ( yacyURL url ) {
this ( url . getFileName ( ) ) ;
this . url = url ;
}
public yacyVersion ( String release ) {
// parse a release file name
// the have the following form:
// yacy_dev_v${releaseVersion}_${DSTAMP}_${releaseNr}.tar.gz
// yacy_v${releaseVersion}_${DSTAMP}_${releaseNr}.tar.gz
// i.e. yacy_v0.51_20070321_3501.tar.gz
this . url = null ;
this . name = release ;
if ( ( release = = null ) | | ( ! release . endsWith ( ".tar.gz" ) ) ) {
throw new RuntimeException ( "release file name '" + release + "' is not valid, no tar.gz" ) ;
}
// cut off tail
release = release . substring ( 0 , release . length ( ) - 7 ) ;
if ( release . startsWith ( "yacy_pro_v" ) ) {
fullRelease = true ;
release = release . substring ( 10 ) ;
} else if ( release . startsWith ( "yacy_emb_v" ) ) {
fullRelease = false ;
release = release . substring ( 10 ) ;
} else if ( release . startsWith ( "yacy_v" ) ) {
fullRelease = false ;
release = release . substring ( 6 ) ;
} else {
throw new RuntimeException ( "release file name '" + release + "' is not valid, wrong prefix" ) ;
}
// now all release names have the form
// ${releaseVersion}_${DSTAMP}_${releaseNr}
String [ ] comp = release . split ( "_" ) ; // should be 3 parts
if ( comp . length ! = 3 ) {
throw new RuntimeException ( "release file name '" + release + "' is not valid, 3 information parts expected" ) ;
}
try {
this . releaseNr = Float . parseFloat ( comp [ 0 ] ) ;
} catch ( NumberFormatException e ) {
throw new RuntimeException ( "release file name '" + release + "' is not valid, '" + comp [ 0 ] + "' should be a float number" ) ;
}
this . mainRelease = ( ( int ) ( this . releaseNr * ( float ) 1000 ) ) % 10 = = 0 ;
//System.out.println("Release version " + this.releaseNr + " is " + ((this.mainRelease) ? "main" : "std"));
this . dateStamp = comp [ 1 ] ;
if ( this . dateStamp . length ( ) ! = 8 ) {
throw new RuntimeException ( "release file name '" + release + "' is not valid, '" + comp [ 1 ] + "' should be a 8-digit date string" ) ;
}
try {
this . svn = Integer . parseInt ( comp [ 2 ] ) ;
} catch ( NumberFormatException e ) {
throw new RuntimeException ( "release file name '" + release + "' is not valid, '" + comp [ 2 ] + "' should be a integer number" ) ;
}
if ( ( this . releaseNr > YACY_STANDARDREL_IS_PRO ) & & ( release . startsWith ( "yacy_v" ) ) ) {
// patch for new release strategy
this . fullRelease = true ;
}
// finished! we parsed a relase string
}
public int compareTo ( Object obj ) {
// returns 0 if this object is equal to the obj, -1 if this is smaller than obj and 1 if this is greater than obj
yacyVersion v = ( yacyVersion ) obj ;
return compare ( this , v ) ;
}
public int compare ( Object arg0 , Object arg1 ) {
// compare-operator for two yacyVersion objects
// must be implemented to make it possible to put this object into
// a ordered structure, like TreeSet or TreeMap
yacyVersion a0 = ( yacyVersion ) arg0 , a1 = ( yacyVersion ) arg1 ;
return ( new Integer ( a0 . svn ) ) . compareTo ( new Integer ( a1 . svn ) ) ;
}
public boolean equals ( Object obj ) {
yacyVersion v = ( yacyVersion ) obj ;
return ( this . svn = = v . svn ) & & ( this . url . toNormalform ( true , true ) . equals ( v . url . toNormalform ( true , true ) ) ) ;
}
public int hashCode ( ) {
return this . url . toNormalform ( true , true ) . hashCode ( ) ;
}
public String toAnchor ( ) {
// generates an anchor string that can be used to embed in an html for direct download
return "<a href=" + this . url . toNormalform ( true , true ) + ">YaCy " + ( ( this . fullRelease ) ? "standard/full release" : "embedded release" ) + " v" + this . releaseNr + ", SVN " + this . svn + "</a>" ;
}
// static methods:
public static final yacyVersion thisVersion ( ) {
// construct a virtual release name for this release
if ( thisVersion = = null ) {
plasmaSwitchboard sb = plasmaSwitchboard . getSwitchboard ( ) ;
boolean full = new File ( sb . getRootPath ( ) , "libx" ) . exists ( ) ;
thisVersion = new yacyVersion (
"yacy" + ( ( full ) ? "" : "_emb" ) +
"_v" + sb . getConfig ( "version" , "0.1" ) + "_" +
sb . getConfig ( "vdate" , "19700101" ) + "_" +
sb . getConfig ( "svnRevision" , "0" ) + ".tar.gz" ) ;
}
return thisVersion ;
}
public static final yacyVersion rulebasedUpdateInfo ( boolean manual ) {
// according to update properties, decide if we should retrieve update information
// if true, the release that can be obtained is returned.
// if false, null is returned
plasmaSwitchboard sb = plasmaSwitchboard . getSwitchboard ( ) ;
// check if update process allows update retrieve
String process = sb . getConfig ( "update.process" , "manual" ) ;
if ( ( ! manual ) & & ( ! process . equals ( "auto" ) ) ) {
yacyCore . log . logInfo ( "rulebasedUpdateInfo: not an automatic update selected" ) ;
return null ; // no, its a manual or guided process
}
// check if the last retrieve time is a minimum time ago
long cycle = Math . max ( 1 , sb . getConfigLong ( "update.cycle" , 168 ) ) * 60 * 60 * 1000 ; // update.cycle is hours
long timeLookup = sb . getConfigLong ( "update.time.lookup" , System . currentTimeMillis ( ) ) ;
if ( ( ! manual ) & & ( timeLookup + cycle > System . currentTimeMillis ( ) ) ) {
yacyCore . log . logInfo ( "rulebasedUpdateInfo: too early for a lookup for a new release (timeLookup = " + timeLookup + ", cycle = " + cycle + ", now = " + System . currentTimeMillis ( ) + ")" ) ;
return null ; // no we have recently made a lookup
}
// check if we know that there is a release that is more recent than that which we are using
TreeSet [ ] releasess = yacyVersion . allReleases ( true ) ; // {0=promain, 1=prodev, 2=stdmain, 3=stddev}
boolean full = new File ( sb . getRootPath ( ) , "libx" ) . exists ( ) ;
yacyVersion latestmain = ( releasess [ ( full ) ? 0 : 2 ] . size ( ) = = 0 ) ? null : ( yacyVersion ) releasess [ ( full ) ? 0 : 2 ] . last ( ) ;
yacyVersion latestdev = ( releasess [ ( full ) ? 1 : 3 ] . size ( ) = = 0 ) ? null : ( yacyVersion ) releasess [ ( full ) ? 1 : 3 ] . last ( ) ;
String concept = sb . getConfig ( "update.concept" , "any" ) ;
String blacklist = sb . getConfig ( "update.blacklist" , ".\\...[123]" ) ;
if ( ( manual ) | | ( concept . equals ( "any" ) ) ) {
// return a dev-release or a main-release
if ( ( latestdev ! = null ) & &
( ( latestmain = = null ) | | ( latestdev . compareTo ( latestmain ) > 0 ) ) & &
( ! ( Float . toString ( latestdev . releaseNr ) . matches ( blacklist ) ) ) ) {
// consider a dev-release
if ( latestdev . compareTo ( thisVersion ( ) ) > 0 ) {
return latestdev ;
} else {
yacyCore . log . logInfo (
"rulebasedUpdateInfo: latest dev " + latestdev . name +
" is not more recent than installed release " + thisVersion ( ) . name ) ;
return null ;
}
}
if ( latestmain ! = null ) {
// consider a main release
if ( ( Float . toString ( latestmain . releaseNr ) . matches ( blacklist ) ) ) {
yacyCore . log . logInfo (
"rulebasedUpdateInfo: latest dev " + latestdev . name +
" matches with blacklist '" + blacklist + "'" ) ;
return null ;
}
if ( latestmain . compareTo ( thisVersion ( ) ) > 0 ) return latestmain ; else {
yacyCore . log . logInfo (
"rulebasedUpdateInfo: latest main " + latestmain . name +
" is not more recent than installed release (1) " + thisVersion ( ) . name ) ;
return null ;
}
}
}
if ( ( concept . equals ( "main" ) ) & & ( latestmain ! = null ) ) {
// return a main-release
if ( ( Float . toString ( latestmain . releaseNr ) . matches ( blacklist ) ) ) {
yacyCore . log . logInfo (
"rulebasedUpdateInfo: latest main " + latestmain . name +
" matches with blacklist'" + blacklist + "'" ) ;
return null ;
}
if ( latestmain . compareTo ( thisVersion ( ) ) > 0 ) return latestmain ; else {
yacyCore . log . logInfo (
"rulebasedUpdateInfo: latest main " + latestmain . name +
" is not more recent than installed release (2) " + thisVersion ( ) . name ) ;
return null ;
}
}
yacyCore . log . logInfo ( "rulebasedUpdateInfo: failed to find more recent release" ) ;
return null ;
}
public static TreeSet [ ] allReleases ( boolean force ) {
// join the release infos
// {promainreleases, prodevreleases, stdmainreleases, stddevreleases}
Object [ ] a = new Object [ latestReleaseLocations . size ( ) ] ;
for ( int j = 0 ; j < latestReleaseLocations . size ( ) ; j + + ) {
a [ j ] = getReleases ( ( yacyURL ) latestReleaseLocations . get ( j ) , force ) ;
}
TreeSet [ ] r = new TreeSet [ 4 ] ;
TreeSet s ;
for ( int i = 0 ; i < 4 ; i + + ) {
s = new TreeSet ( ) ;
for ( int j = 0 ; j < a . length ; j + + ) {
if ( ( a [ j ] ! = null ) & & ( ( ( TreeSet [ ] ) a [ j ] ) [ i ] ! = null ) ) s . addAll ( ( ( TreeSet [ ] ) a [ j ] ) [ i ] ) ;
}
r [ i ] = s ;
}
return r ;
}
private static TreeSet [ ] getReleases ( yacyURL location , boolean force ) {
// get release info from a internet resource
// {promainreleases, prodevreleases, stdmainreleases, stddevreleases}
TreeSet [ ] latestRelease = ( TreeSet [ ] ) latestReleases . get ( location ) ;
if ( force | |
( latestRelease = = null ) / * | |
( ( latestRelease [ 0 ] . size ( ) = = 0 ) & &
( latestRelease [ 1 ] . size ( ) = = 0 ) & &
( latestRelease [ 2 ] . size ( ) = = 0 ) & &
( latestRelease [ 3 ] . size ( ) = = 0 ) ) * / ) {
latestRelease = allReleaseFrom ( location ) ;
latestReleases . put ( location , latestRelease ) ;
}
return latestRelease ;
}
private static TreeSet [ ] allReleaseFrom ( yacyURL url ) {
// retrieves the latest info about releases
// this is done by contacting a release location,
// parsing the content and filtering+parsing links
// returns the version info if successful, null otherwise
htmlFilterContentScraper scraper ;
try {
scraper = htmlFilterContentScraper . parseResource ( url ) ;
} catch ( IOException e ) {
return null ;
}
// analyse links in scraper resource, and find link to latest release in it
Map anchors = scraper . getAnchors ( ) ; // a url (String) / name (String) relation
Iterator i = anchors . keySet ( ) . iterator ( ) ;
TreeSet stddevreleases = new TreeSet ( ) ;
TreeSet prodevreleases = new TreeSet ( ) ;
TreeSet stdmainreleases = new TreeSet ( ) ;
TreeSet promainreleases = new TreeSet ( ) ;
yacyVersion release ;
while ( i . hasNext ( ) ) {
try {
url = new yacyURL ( ( String ) i . next ( ) , null ) ;
} catch ( MalformedURLException e1 ) {
continue ; // just ignore invalid urls
}
try {
release = new yacyVersion ( url ) ;
//System.out.println("r " + release.toAnchor());
if ( release . fullRelease & & release . mainRelease ) promainreleases . add ( release ) ;
if ( release . fullRelease & & ! release . mainRelease ) prodevreleases . add ( release ) ;
if ( ! release . fullRelease & & release . mainRelease ) stdmainreleases . add ( release ) ;
if ( ! release . fullRelease & & ! release . mainRelease ) stddevreleases . add ( release ) ;
} catch ( RuntimeException e ) {
// the release string was not well-formed.
// that might have been another link
// just dont care
continue ;
}
}
plasmaSwitchboard . getSwitchboard ( ) . setConfig ( "update.time.lookup" , System . currentTimeMillis ( ) ) ;
return new TreeSet [ ] { promainreleases , prodevreleases , stdmainreleases , stddevreleases } ;
}
public static void downloadRelease ( yacyVersion release ) throws IOException {
File storagePath = plasmaSwitchboard . getSwitchboard ( ) . releasePath ;
// load file
File download = new File ( storagePath , release . url . getFileName ( ) ) ;
httpc . wget (
release . url ,
release . url . getHost ( ) ,
300000 ,
null ,
null ,
plasmaSwitchboard . getSwitchboard ( ) . remoteProxyConfig ,
null ,
download
) ;
if ( ( ! download . exists ( ) ) | | ( download . length ( ) = = 0 ) ) throw new IOException ( "wget of url " + release . url + " failed" ) ;
plasmaSwitchboard . getSwitchboard ( ) . setConfig ( "update.time.download" , System . currentTimeMillis ( ) ) ;
}
public static void restart ( ) {
plasmaSwitchboard sb = plasmaSwitchboard . getSwitchboard ( ) ;
if ( System . getProperty ( "os.name" ) . toLowerCase ( ) . startsWith ( "win" ) ) {
// create yacy.restart file which is used in Windows startscript
final File yacyRestart = new File ( sb . getRootPath ( ) , "DATA/yacy.restart" ) ;
if ( ! yacyRestart . exists ( ) ) {
try {
yacyRestart . createNewFile ( ) ;
plasmaSwitchboard . getSwitchboard ( ) . terminate ( 5000 ) ;
} catch ( IOException e ) {
serverLog . logSevere ( "SHUTDOWN" , "restart failed" , e ) ;
}
}
}
if ( serverSystem . canExecUnix ) {
// start a re-start daemon
try {
serverLog . logInfo ( "RESTART" , "INITIATED" ) ;
String script =
"#!/bin/sh" + serverCore . LF_STRING +
"cd " + sb . getRootPath ( ) + "/DATA/RELEASE/" + serverCore . LF_STRING +
"while [ -f ../yacy.running ]; do" + serverCore . LF_STRING +
"sleep 1" + serverCore . LF_STRING +
"done" + serverCore . LF_STRING +
"cd ../../" + serverCore . LF_STRING +
"nohup ./startYACY.sh > /dev/null" + serverCore . LF_STRING ;
File scriptFile = new File ( sb . getRootPath ( ) , "DATA/RELEASE/restart.sh" ) ;
serverSystem . deployScript ( scriptFile , script ) ;
serverLog . logInfo ( "RESTART" , "wrote restart-script to " + scriptFile . getAbsolutePath ( ) ) ;
serverSystem . execAsynchronous ( scriptFile ) ;
serverLog . logInfo ( "RESTART" , "script is running" ) ;
sb . terminate ( 5000 ) ;
} catch ( IOException e ) {
serverLog . logSevere ( "RESTART" , "restart failed" , e ) ;
}
}
}
public static void deployRelease ( String release ) {
//byte[] script = ("cd " + plasmaSwitchboard.getSwitchboard().getRootPath() + ";while [ -e ../yacy.running ]; do sleep 1;done;tar xfz " + release + ";cp -Rf yacy/* ../../;rm -Rf yacy;cd ../../;startYACY.sh").getBytes();
try {
plasmaSwitchboard sb = plasmaSwitchboard . getSwitchboard ( ) ;
serverLog . logInfo ( "UPDATE" , "INITIATED" ) ;
String script =
"#!/bin/sh" + serverCore . LF_STRING +
"cd " + sb . getRootPath ( ) + "/DATA/RELEASE/" + serverCore . LF_STRING +
"gunzip -c " + release + " | tar xf -" + serverCore . LF_STRING +
"while [ -f ../yacy.running ]; do" + serverCore . LF_STRING +
"sleep 1" + serverCore . LF_STRING +
"done" + serverCore . LF_STRING +
"cp -Rf yacy/* ../../" + serverCore . LF_STRING +
"rm -Rf yacy" + serverCore . LF_STRING +
"cd ../../" + serverCore . LF_STRING +
"nohup ./startYACY.sh > /dev/null" + serverCore . LF_STRING ;
File scriptFile = new File ( sb . getRootPath ( ) , "DATA/RELEASE/update.sh" ) ;
serverSystem . deployScript ( scriptFile , script ) ;
serverLog . logInfo ( "UPDATE" , "wrote update-script to " + scriptFile . getAbsolutePath ( ) ) ;
serverSystem . execAsynchronous ( scriptFile ) ;
serverLog . logInfo ( "UPDATE" , "script is running" ) ;
sb . setConfig ( "update.time.deploy" , System . currentTimeMillis ( ) ) ;
sb . terminate ( 5000 ) ;
} catch ( IOException e ) {
serverLog . logSevere ( "UPDATE" , "update failed" , e ) ;
}
}
/ * *
* Converts combined version - string to a pretty string , e . g . "0.435/01818" or "dev/01818" ( development version ) or "dev/00000" ( in case of wrong input )
*
* @param ver Combined version string matching regular expression : "\A(\d+\.\d{3})(\d{4}|\d{5})\z" < br >
* ( i . e . : start of input , 1 or more digits in front of decimal point , decimal point followed by 3 digits as major version , 4 or 5 digits for SVN - Version , end of input )
* @return If the major version is & lt ; 0.11 - major version is separated from SVN - version by '/' , e . g . "0.435/01818" < br >
* If the major version is & gt ; = 0.11 - major version is replaced by "dev" and separated SVN - version by '/' , e . g . "dev/01818" < br >
* "dev/00000" - If the input does not matcht the regular expression above
* /
public static String combined2prettyVersion ( String ver ) {
return combined2prettyVersion ( ver , "" ) ;
}
public static String combined2prettyVersion ( String ver , String computerName ) {
final Matcher matcher = Pattern . compile ( "\\A(\\d+\\.\\d{1,3})(\\d{0,5})\\z" ) . matcher ( ver ) ;
if ( ! matcher . find ( ) ) {
serverLog . logWarning ( "STARTUP" , "Peer '" + computerName + "': wrong format of version-string: '" + ver + "'. Using default string 'dev/00000' instead" ) ;
return "dev/00000" ;
}
String mainversion = ( Double . parseDouble ( matcher . group ( 1 ) ) < 0.11 ? "dev" : matcher . group ( 1 ) ) ;
String revision = matcher . group ( 2 ) ;
for ( int i = revision . length ( ) ; i < 5 ; + + i ) revision + = "0" ;
return mainversion + "/" + revision ;
}
/ * *
* Combines the version of YaCy with the versionnumber from SVN to a
* combined version
*
* @param version Current given version .
* @param svn Current version given from SVN .
* @return String with the combined version .
* /
public static double versvn2combinedVersion ( double v , int svn ) {
return ( Math . rint ( ( v * 100000000.0 ) + ( ( double ) svn ) ) / 100000000 ) ;
}
public static void main ( String [ ] args ) {
float base = ( float ) 0.53 ;
String blacklist = "....[123]" ;
String test ;
for ( int i = 0 ; i < 20 ; i + + ) {
test = Float . toString ( base + ( ( ( float ) i ) / 1000 ) ) ;
System . out . println ( test + " is " + ( ( test . matches ( blacklist ) ) ? "blacklisted" : " not blacklisted" ) ) ;
}
}
}