2013-09-22 02:21:24 +02:00
// yacy.java
// -----------------------
// (C) by Michael Peter Christen; mc@yacy.net
// first published on http://www.yacy.net
// Frankfurt, Germany, 2004, 2005
//
// $LastChangedDate$
// $LastChangedRevision$
// $LastChangedBy$
//
// 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 net.yacy ;
import java.io.BufferedReader ;
import java.io.BufferedWriter ;
import java.io.File ;
import java.io.FileInputStream ;
import java.io.FileNotFoundException ;
import java.io.FileOutputStream ;
import java.io.FileWriter ;
import java.io.IOException ;
import java.io.InputStreamReader ;
import java.io.PrintWriter ;
import java.io.RandomAccessFile ;
2021-10-24 20:49:59 +02:00
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
2013-09-22 02:21:24 +02:00
import java.nio.channels.FileChannel ;
import java.nio.channels.FileLock ;
2016-08-23 00:04:24 +02:00
import java.util.ArrayList ;
2017-03-26 11:48:00 +02:00
import java.util.LinkedHashMap ;
2016-08-23 00:04:24 +02:00
import java.util.List ;
2017-06-27 06:42:33 +02:00
import java.util.Locale ;
2017-03-26 11:48:00 +02:00
import java.util.Map ;
2013-09-22 02:21:24 +02:00
import java.util.Properties ;
2014-05-11 01:53:11 +02:00
import java.util.concurrent.Semaphore ;
2016-12-28 09:47:27 +01:00
import java.util.concurrent.TimeUnit ;
2016-08-17 15:15:31 +02:00
2017-03-26 11:48:00 +02:00
import org.apache.http.Header ;
import org.apache.http.HttpStatus ;
import org.apache.http.entity.mime.content.ContentBody ;
2016-08-17 15:15:31 +02:00
import com.google.common.io.Files ;
2013-09-22 02:21:24 +02:00
import net.yacy.cora.date.GenericFormatter ;
2017-03-26 11:48:00 +02:00
import net.yacy.cora.document.encoding.UTF8 ;
import net.yacy.cora.document.id.MultiProtocolURL ;
2018-05-29 09:24:21 +02:00
import net.yacy.cora.federate.solr.instance.RemoteInstance ;
2021-10-24 20:49:59 +02:00
import net.yacy.cora.plugin.ClassProvider ;
2013-09-22 02:21:24 +02:00
import net.yacy.cora.protocol.ClientIdentification ;
2016-08-17 15:15:31 +02:00
import net.yacy.cora.protocol.ConnectionInfo ;
2017-03-26 11:48:00 +02:00
import net.yacy.cora.protocol.HeaderFramework ;
2013-09-22 02:21:24 +02:00
import net.yacy.cora.protocol.TimeoutRequest ;
import net.yacy.cora.protocol.http.HTTPClient ;
import net.yacy.cora.util.ConcurrentLog ;
2017-03-26 11:48:00 +02:00
import net.yacy.data.TransactionManager ;
2016-08-23 00:04:24 +02:00
import net.yacy.data.Translator ;
2013-09-22 02:21:24 +02:00
import net.yacy.gui.YaCyApp ;
import net.yacy.gui.framework.Browser ;
2013-10-09 00:40:48 +02:00
import net.yacy.http.YaCyHttpServer ;
2013-09-22 02:21:24 +02:00
import net.yacy.kelondro.util.FileUtils ;
import net.yacy.kelondro.util.Formatter ;
import net.yacy.kelondro.util.MemoryControl ;
import net.yacy.kelondro.util.OS ;
2016-08-17 15:15:31 +02:00
import net.yacy.peers.Seed ;
2013-09-22 02:21:24 +02:00
import net.yacy.peers.operation.yacyBuildProperties ;
import net.yacy.peers.operation.yacyRelease ;
import net.yacy.search.Switchboard ;
import net.yacy.search.SwitchboardConstants ;
2014-02-24 05:16:31 +01:00
import net.yacy.server.serverSwitch ;
2016-06-03 01:46:30 +02:00
import net.yacy.utils.translation.TranslatorXliff ;
2013-09-22 02:21:24 +02:00
/ * *
2022-10-04 15:31:47 +02:00
* This is the main class of YaCy . Several threads are started from here :
* < ul >
* < li > one single instance of the plasmaSwitchboard is generated , which itself
* starts a thread with a plasmaHTMLCache object . This object simply counts
* files sizes in the cache and terminates them . It also generates a
* plasmaCrawlerLoader object , which may itself start some more httpc - calling
* threads to load web pages . They terminate automatically when a page has
* loaded .
* < li > one serverCore - thread is started , which implements a multi - threaded
* server . The process may start itself many more processes that handle
* connections . lo
* < li > finally , all idle - dependent processes are written in a queue in
* plasmaSwitchboard which are worked off inside an idle - sensitive loop of the
* main process . ( here )
* < / ul >
*
* On termination , the following must be done :
* < ul >
* < li > stop feeding of the crawling process because it otherwise fills the
* indexing queue .
* < li > say goodbye to connected peers and disable new connections . Don ' t wait for
* success .
* < li > first terminate the serverCore thread . This prevents that new cache
* objects are queued .
* < li > wait that the plasmaHTMLCache terminates ( it should be normal that this
* process already has terminated ) .
* < li > then wait for termination of all loader process of the
* plasmaCrawlerLoader .
* < li > work off the indexing and cache storage queue . These values are inside a
* RAM cache and would be lost otherwise .
* < li > write all settings .
* < li > terminate .
* < / ul >
* /
2013-09-22 02:21:24 +02:00
public final class yacy {
// static objects
public static final String vString = yacyBuildProperties . getVersion ( ) ;
2022-07-10 11:13:34 +02:00
public static final String copyright = " [ YaCy v " + vString + " by Michael Christen / www.yacy.net ] " ;
2013-09-22 02:21:24 +02:00
public static final String hline = " ------------------------------------------------------------------------------- " ;
public static final Semaphore shutdownSemaphore = new Semaphore ( 0 ) ;
2016-03-11 18:12:13 +01:00
public static File htDocsPath = null ;
public static File shareDefaultPath = null ;
public static File shareDumpDefaultPath = null ;
2021-10-24 20:49:59 +02:00
2013-09-22 02:21:24 +02:00
/ * *
* a reference to the { @link Switchboard } created by the
* { @link yacy # startup ( String , long , long ) } method .
* /
private static Switchboard sb = null ;
/ * *
2022-10-04 15:31:47 +02:00
* Starts up the whole application . Sets up all datastructures and starts
* the main threads .
*
* @param homePath Root - path where all information is to be found .
* @param startupFree free memory at startup time , to be used later for statistics
* /
2013-09-22 02:21:24 +02:00
private static void startup ( final File dataHome , final File appHome , final long startupMemFree , final long startupMemTotal , final boolean gui ) {
2015-11-17 22:27:07 +01:00
String tmpdir = null ;
2013-09-22 02:21:24 +02:00
try {
// start up
System . out . println ( copyright ) ;
System . out . println ( hline ) ;
// ensure that there is a DATA directory, if not, create one and if that fails warn and die
mkdirsIfNeseccary ( dataHome ) ;
mkdirsIfNeseccary ( appHome ) ;
File f = new File ( dataHome , " DATA/ " ) ;
mkdirsIfNeseccary ( f ) ;
2021-01-24 22:50:37 +01:00
if ( ! ( f . exists ( ) ) ) {
System . err . println ( " Error creating DATA-directory in " + dataHome . toString ( ) + " . Please check your write-permission for this folder. YaCy will now terminate. " ) ;
System . exit ( - 1 ) ;
}
2018-09-13 12:17:02 +02:00
// set jvm tmpdir to a subdir for easy cleanup (as extensive use file.deleteonexit waists memory during long runs, as todelete files names are collected and never cleaned up during runtime)
2021-10-24 20:49:59 +02:00
// keep this as earlier as possible, as any other class can use the "java.io.tmpdir" property, even the log manager, when the log file pattern uses "%t" as an alias for the tmp directory
try {
2018-09-13 12:17:02 +02:00
tmpdir = java . nio . file . Files . createTempDirectory ( " yacy-tmp- " ) . toString ( ) ; // creates sub dir in jvm's temp (see System.property "java.io.tempdir")
System . setProperty ( " java.io.tmpdir " , tmpdir ) ;
2021-10-24 20:49:59 +02:00
} catch ( final IOException ex ) { }
2013-09-22 02:21:24 +02:00
// setting up logging
2021-01-04 20:46:21 +01:00
f = new File ( dataHome , " DATA/LOG/ " ) ;
2020-12-29 20:19:58 +01:00
mkdirsIfNeseccary ( f ) ;
2021-01-04 20:46:21 +01:00
f = new File ( f , " yacy.logging " ) ;
2021-01-24 22:50:37 +01:00
final File f0 = new File ( appHome , " defaults/yacy.logging " ) ;
if ( ! f . exists ( ) | | f0 . lastModified ( ) > f . lastModified ( ) ) try {
Files . copy ( f0 , f ) ;
2013-09-22 02:21:24 +02:00
} catch ( final IOException e ) {
2020-12-29 20:19:58 +01:00
System . out . println ( " could not copy yacy.logging: " + e . getMessage ( ) ) ;
2013-09-22 02:21:24 +02:00
}
try {
2018-09-13 12:17:02 +02:00
ConcurrentLog . configureLogging ( dataHome , new File ( dataHome , " DATA/LOG/yacy.logging " ) ) ;
2013-09-22 02:21:24 +02:00
} catch ( final IOException e ) {
System . out . println ( " could not find logging properties in homePath= " + dataHome ) ;
ConcurrentLog . logException ( e ) ;
}
2022-10-04 15:31:47 +02:00
ConcurrentLog . config ( " STARTUP " , " YaCy version: " + yacyBuildProperties . getReleaseStub ( ) ) ;
2013-09-22 02:21:24 +02:00
ConcurrentLog . config ( " STARTUP " , " Java version: " + System . getProperty ( " java.version " , " no-java-version " ) ) ;
ConcurrentLog . config ( " STARTUP " , " Operation system: " + System . getProperty ( " os.name " , " unknown " ) ) ;
ConcurrentLog . config ( " STARTUP " , " Application root-path: " + appHome ) ;
ConcurrentLog . config ( " STARTUP " , " Data root-path: " + dataHome ) ;
ConcurrentLog . config ( " STARTUP " , " Time zone: UTC " + GenericFormatter . UTCDiffString ( ) + " ; UTC+0000 is " + System . currentTimeMillis ( ) ) ;
ConcurrentLog . config ( " STARTUP " , " Maximum file system path length: " + OS . maxPathLength ) ;
f = new File ( dataHome , " DATA/yacy.running " ) ;
if ( ! f . createNewFile ( ) ) ConcurrentLog . severe ( " STARTUP " , " WARNING: the file " + f + " can not be created! " ) ;
2022-10-04 22:05:32 +02:00
try {
FileOutputStream fos = new FileOutputStream ( f ) ;
fos . write ( Integer . toString ( OS . getPID ( ) ) . getBytes ( ) ) ;
fos . close ( ) ;
} catch ( final Exception e ) { } // write PID
2013-09-22 02:21:24 +02:00
f . deleteOnExit ( ) ;
FileChannel channel = null ;
FileLock lock = null ;
try {
2022-10-04 22:05:32 +02:00
RandomAccessFile raf = new RandomAccessFile ( f , " rw " ) ;
channel = raf . getChannel ( ) ;
2021-01-24 22:50:37 +01:00
lock = channel . tryLock ( ) ; // lock yacy.running
2022-10-04 22:05:32 +02:00
raf . close ( ) ;
2013-09-22 02:21:24 +02:00
} catch ( final Exception e ) { }
2021-01-24 22:50:37 +01:00
final String conf = " DATA/SETTINGS/yacy.conf " . replace ( " / " , File . separator ) ;
2013-09-22 02:21:24 +02:00
try {
sb = new Switchboard ( dataHome , appHome , " defaults/yacy.init " . replace ( " / " , File . separator ) , conf ) ;
} catch ( final RuntimeException e ) {
ConcurrentLog . severe ( " STARTUP " , " YaCy cannot start: " + e . getMessage ( ) , e ) ;
System . exit ( - 1 ) ;
}
//sbSync.V(); // signal that the sb reference was set
// switch the memory strategy
MemoryControl . setStandardStrategy ( sb . getConfigBool ( " memory.standardStrategy " , true ) ) ;
// save information about available memory at startup time
sb . setConfig ( " memoryFreeAfterStartup " , startupMemFree ) ;
sb . setConfig ( " memoryTotalAfterStartup " , startupMemTotal ) ;
// start gui if wanted
2015-10-31 23:09:03 +01:00
if ( gui ) YaCyApp . start ( " localhost " , sb . getLocalPort ( ) ) ;
2013-09-22 02:21:24 +02:00
// hardcoded, forced, temporary value-migration
sb . setConfig ( " htTemplatePath " , " htroot/env/templates " ) ;
2016-03-20 03:34:28 +01:00
double oldVer ;
2021-01-24 22:50:37 +01:00
try {
2016-03-20 03:34:28 +01:00
String tmpversion = sb . getConfig ( Seed . VERSION , " " ) ;
if ( tmpversion . isEmpty ( ) ) { // before 1.83009737 only the svnRevision nr was in config (like 9737)
tmpversion = yacyBuildProperties . getVersion ( ) ;
2021-10-24 20:49:59 +02:00
final int oldRev = Integer . parseInt ( sb . getConfig ( " svnRevision " , " 0 " ) ) ;
if ( oldRev > 1 ) {
2016-03-20 03:34:28 +01:00
oldVer = Double . parseDouble ( tmpversion ) + oldRev / 100000000 . 0 ;
} else {
2022-10-04 15:31:47 +02:00
oldVer = Double . parseDouble ( yacyBuildProperties . getVersion ( ) ) ; // failsafe (assume current version = no migration)
2016-03-20 03:34:28 +01:00
}
} else {
oldVer = Double . parseDouble ( tmpversion ) ;
}
2013-09-22 02:21:24 +02:00
} catch ( final NumberFormatException e ) {
2016-03-20 03:34:28 +01:00
oldVer = 0 . 0d ;
2021-01-24 22:50:37 +01:00
}
2022-10-04 15:31:47 +02:00
final double newRev = Double . parseDouble ( yacyBuildProperties . getVersion ( ) ) ;
sb . setConfig ( Seed . VERSION , yacyBuildProperties . getVersion ( ) ) ;
2013-09-22 02:21:24 +02:00
sb . setConfig ( " applicationRoot " , appHome . toString ( ) ) ;
sb . setConfig ( " dataRoot " , dataHome . toString ( ) ) ;
// create some directories
2014-02-27 00:23:34 +01:00
final File htRootPath = new File ( appHome , sb . getConfig ( SwitchboardConstants . HTROOT_PATH , SwitchboardConstants . HTROOT_PATH_DEFAULT ) ) ;
mkdirIfNeseccary ( htRootPath ) ;
2016-03-11 18:12:13 +01:00
htDocsPath = sb . getDataPath ( SwitchboardConstants . HTDOCS_PATH , SwitchboardConstants . HTDOCS_PATH_DEFAULT ) ;
2013-09-22 02:21:24 +02:00
mkdirIfNeseccary ( htDocsPath ) ;
//final File htTemplatePath = new File(homePath, sb.getConfig("htTemplatePath","htdocs"));
// create default notifier picture
2021-10-24 20:49:59 +02:00
final File notifierFile = new File ( htDocsPath , " notifier.gif " ) ;
2013-10-27 15:12:17 +01:00
if ( ! notifierFile . exists ( ) ) try { Files . copy ( new File ( htRootPath , " env/grafics/empty.gif " ) , notifierFile ) ; } catch ( final IOException e ) { }
2013-09-22 02:21:24 +02:00
final File htdocsReadme = new File ( htDocsPath , " readme.txt " ) ;
if ( ! ( htdocsReadme . exists ( ) ) ) try { FileUtils . copy ( (
" This is your root directory for individual Web Content \ r \ n " +
2022-10-04 15:31:47 +02:00
" \ r \ n " +
" Please place your html files into the www subdirectory. \ r \ n " +
" The URL of that path is either \ r \ n " +
" http://www.<your-peer-name>.yacy or \ r \ n " +
" http://<your-ip>:<your-port>/www \ r \ n " +
" \ r \ n " +
" Other subdirectories may be created; they map to corresponding sub-domains. \ r \ n " +
" This directory shares it's content with the applications htroot path, so you \ r \ n " +
" may access your yacy search page with \ r \ n " +
" http://<your-peer-name>.yacy/ \ r \ n " +
2013-09-22 02:21:24 +02:00
" \ r \ n " ) . getBytes ( ) , htdocsReadme ) ; } catch ( final IOException e ) {
System . out . println ( " Error creating htdocs readme: " + e . getMessage ( ) ) ;
}
2016-03-11 18:12:13 +01:00
shareDefaultPath = new File ( htDocsPath , " share " ) ;
2013-09-22 02:21:24 +02:00
mkdirIfNeseccary ( shareDefaultPath ) ;
2016-03-11 18:12:13 +01:00
shareDumpDefaultPath = new File ( shareDefaultPath , " dump " ) ;
mkdirIfNeseccary ( shareDumpDefaultPath ) ;
2013-09-22 02:21:24 +02:00
2016-03-20 03:34:28 +01:00
migration . migrate ( sb , oldVer , newRev ) ;
2013-09-22 02:21:24 +02:00
// delete old release files
final int deleteOldDownloadsAfterDays = ( int ) sb . getConfigLong ( " update.deleteOld " , 30 ) ;
yacyRelease . deleteOldDownloads ( sb . releasePath , deleteOldDownloadsAfterDays ) ;
// start main threads
2015-10-31 23:09:03 +01:00
final int port = sb . getLocalPort ( ) ;
2013-09-22 02:21:24 +02:00
try {
2013-10-17 03:34:22 +02:00
// start http server
2021-01-24 22:50:37 +01:00
YaCyHttpServer httpServer ;
2023-06-29 10:46:05 +02:00
httpServer = new YaCyHttpServer ( port , " 0.0.0.0 " ) ;
2013-10-17 03:34:22 +02:00
httpServer . startupServer ( ) ;
2013-10-09 00:40:48 +02:00
sb . setHttpServer ( httpServer ) ;
2015-03-29 07:12:23 +02:00
// TODO: this has no effect on Jetty (but needed to reflect configured value and limit is still used)
ConnectionInfo . setServerMaxcount ( sb . getConfigInt ( " connectionsMax " , ConnectionInfo . getMaxcount ( ) ) ) ;
2013-10-09 00:40:48 +02:00
ConcurrentLog . info ( " STARTUP " , httpServer . getVersion ( ) ) ;
2020-12-02 18:39:45 +01:00
2013-09-22 02:21:24 +02:00
// open the browser window
final boolean browserPopUpTrigger = sb . getConfig ( SwitchboardConstants . BROWSER_POP_UP_TRIGGER , " true " ) . equals ( " true " ) ;
if ( browserPopUpTrigger ) try {
final String browserPopUpPage = sb . getConfig ( SwitchboardConstants . BROWSER_POP_UP_PAGE , " ConfigBasic.html " ) ;
2014-01-05 04:23:44 +01:00
//boolean properPW = (sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT, "").isEmpty()) && (sb.getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "").length() > 0);
2013-09-22 02:21:24 +02:00
//if (!properPW) browserPopUpPage = "ConfigBasic.html";
2021-10-24 20:49:59 +02:00
/ * YaCy main startup process must not hang because browser opening is long or fails .
2016-09-28 14:52:30 +02:00
* Let ' s open try opening the browser in a separate thread * /
new Thread ( " Browser opening " ) {
2021-01-24 22:50:37 +01:00
@Override
public void run ( ) {
2016-09-28 14:52:30 +02:00
Browser . openBrowser ( ( " http://localhost: " + port ) + " / " + browserPopUpPage ) ;
2021-01-24 22:50:37 +01:00
}
2016-09-28 14:52:30 +02:00
} . start ( ) ;
2022-10-04 15:31:47 +02:00
// Browser.openBrowser((server.withSSL()?"https":"http") + "://localhost:" + serverCore.getPortNr(port) + "/" + browserPopUpPage);
2013-09-22 02:21:24 +02:00
} catch ( final Throwable e ) {
// cannot open browser. This may be normal in headless environments
//Log.logException(e);
}
// enable browser popup, http server is ready now
sb . tray . setReady ( ) ;
//regenerate Locales from Translationlist, if needed
final File locale_source = sb . getAppPath ( " locale.source " , " locales " ) ;
final String lang = sb . getConfig ( " locale.language " , " " ) ;
2016-08-23 00:04:24 +02:00
// on lang=browser all active translation should be checked (because any could be requested by client)
List < String > langlist ;
if ( lang . endsWith ( " browser " ) )
langlist = Translator . activeTranslations ( ) ; // get all translated languages
else {
2021-10-24 20:49:59 +02:00
langlist = new ArrayList < > ( ) ;
2016-08-23 00:04:24 +02:00
langlist . add ( lang ) ;
}
2021-10-24 20:49:59 +02:00
for ( final String tmplang : langlist ) {
2016-08-23 00:04:24 +02:00
if ( ! tmplang . equals ( " " ) & & ! tmplang . equals ( " default " ) & & ! tmplang . equals ( " browser " ) ) { //locale is used
String currentRev = null ;
2017-06-02 12:14:29 +02:00
BufferedReader br = null ;
2016-08-23 00:04:24 +02:00
try {
2017-06-02 12:14:29 +02:00
br = new BufferedReader ( new InputStreamReader ( new FileInputStream ( new File ( sb . getDataPath ( " locale.translated_html " , " DATA/LOCALE/htroot " ) , tmplang + " /version " ) ) ) ) ;
2016-08-23 00:04:24 +02:00
currentRev = br . readLine ( ) ; // may return null
} catch ( final IOException e ) {
//Error
2017-06-02 12:14:29 +02:00
} finally {
2021-01-24 22:50:37 +01:00
try {
br . close ( ) ;
2021-10-24 20:49:59 +02:00
} catch ( final IOException ioe ) {
2021-01-24 22:50:37 +01:00
ConcurrentLog . warn ( " STARTUP " , " Could not close " + tmplang + " version file " ) ;
}
2016-08-23 00:04:24 +02:00
}
2013-09-22 02:21:24 +02:00
2016-08-23 00:04:24 +02:00
if ( currentRev = = null | | ! currentRev . equals ( sb . getConfig ( Seed . VERSION , " " ) ) ) {
try { //is this another version?!
final File sourceDir = new File ( sb . getConfig ( SwitchboardConstants . HTROOT_PATH , SwitchboardConstants . HTROOT_PATH_DEFAULT ) ) ;
final File destDir = new File ( sb . getDataPath ( " locale.translated_html " , " DATA/LOCALE/htroot " ) , tmplang ) ;
if ( new TranslatorXliff ( ) . translateFilesRecursive ( sourceDir , destDir , new File ( locale_source , tmplang + " .lng " ) , " html,template,inc " , " locale " ) ) { //translate it
//write the new Versionnumber
final BufferedWriter bw = new BufferedWriter ( new PrintWriter ( new FileWriter ( new File ( destDir , " version " ) ) ) ) ;
bw . write ( sb . getConfig ( Seed . VERSION , " Error getting Version " ) ) ;
bw . close ( ) ;
}
} catch ( final IOException e ) {
}
2013-09-22 02:21:24 +02:00
}
2016-08-23 00:04:24 +02:00
}
2013-09-22 02:21:24 +02:00
}
// initialize number formatter with this locale
2016-08-23 00:04:24 +02:00
if ( ! lang . equals ( " browser " ) ) // "default" is handled by .setLocale()
Formatter . setLocale ( lang ) ;
2013-09-22 02:21:24 +02:00
// registering shutdown hook
ConcurrentLog . config ( " STARTUP " , " Registering Shutdown Hook " ) ;
final Runtime run = Runtime . getRuntime ( ) ;
2016-12-28 09:47:27 +01:00
run . addShutdownHook ( new shutdownHookThread ( sb , shutdownSemaphore ) ) ;
2013-09-22 02:21:24 +02:00
// save information about available memory after all initializations
//try {
2022-10-04 15:31:47 +02:00
sb . setConfig ( " memoryFreeAfterInitBGC " , MemoryControl . free ( ) ) ;
sb . setConfig ( " memoryTotalAfterInitBGC " , MemoryControl . total ( ) ) ;
System . gc ( ) ;
sb . setConfig ( " memoryFreeAfterInitAGC " , MemoryControl . free ( ) ) ;
sb . setConfig ( " memoryTotalAfterInitAGC " , MemoryControl . total ( ) ) ;
2013-09-22 02:21:24 +02:00
//} catch (final ConcurrentModificationException e) {}
// wait for server shutdown
try {
sb . waitForShutdown ( ) ;
} catch ( final Exception e ) {
ConcurrentLog . severe ( " MAIN CONTROL LOOP " , " PANIC: " + e . getMessage ( ) , e ) ;
}
// shut down
ConcurrentLog . config ( " SHUTDOWN " , " caught termination signal " ) ;
httpServer . stop ( ) ;
ConcurrentLog . config ( " SHUTDOWN " , " server has terminated " ) ;
sb . close ( ) ;
} catch ( final Exception e ) {
ConcurrentLog . severe ( " STARTUP " , " Unexpected Error: " + e . getClass ( ) . getName ( ) , e ) ;
//System.exit(1);
}
2022-10-05 22:13:06 +02:00
if ( lock ! = null & & lock . isValid ( ) ) lock . release ( ) ;
if ( channel ! = null & & channel . isOpen ( ) ) channel . close ( ) ;
2013-09-22 02:21:24 +02:00
} catch ( final Exception ee ) {
ConcurrentLog . severe ( " STARTUP " , " FATAL ERROR: " + ee . getMessage ( ) , ee ) ;
} finally {
}
2015-11-17 22:27:07 +01:00
if ( tmpdir ! = null ) FileUtils . deletedelete ( new File ( tmpdir ) ) ; // clean up temp dir (set at startup as subdir of system.propery "java.io.tmpdir")
2013-09-22 02:21:24 +02:00
ConcurrentLog . config ( " SHUTDOWN " , " goodbye. (this is the last line) " ) ;
ConcurrentLog . shutdown ( ) ;
shutdownSemaphore . release ( 1000 ) ;
try {
System . exit ( 0 ) ;
} catch ( final Exception e ) { } // was once stopped by de.anomic.net.ftpc$sm.checkExit(ftpc.java:1790)
}
2021-01-24 22:50:37 +01:00
/ * *
* @param f
* /
private static void delete ( final File f ) {
if ( ! f . delete ( ) )
ConcurrentLog . severe ( " STARTUP " , " WARNING: the file " + f + " can not be deleted! " ) ;
}
/ * *
* @see File # mkdir ( )
* @param path
* /
private static void mkdirIfNeseccary ( final File path ) {
if ( ! ( path . exists ( ) ) )
if ( ! path . mkdir ( ) )
ConcurrentLog . warn ( " STARTUP " , " could not create directory " + path . toString ( ) ) ;
}
/ * *
* @see File # mkdirs ( )
* @param path
* /
public static void mkdirsIfNeseccary ( final File path ) {
if ( ! ( path . exists ( ) ) )
if ( ! path . mkdirs ( ) )
ConcurrentLog . warn ( " STARTUP " , " could not create directories " + path . toString ( ) ) ;
}
/ * *
2022-10-04 15:31:47 +02:00
* Loads the configuration from the data - folder .
* FIXME : Why is this called over and over again from every method , instead
* of setting the configurationdata once for this class in main ?
*
* @param mes Where are we called from , so that the errormessages can be
* more descriptive .
* @param homePath Root - path where all the information is to be found .
* @return Properties read from the configurationfile .
* /
2013-09-22 02:21:24 +02:00
private static Properties configuration ( final String mes , final File homePath ) {
ConcurrentLog . config ( mes , " Application Root Path: " + homePath . toString ( ) ) ;
// read data folder
final File dataFolder = new File ( homePath , " DATA " ) ;
if ( ! ( dataFolder . exists ( ) ) ) {
ConcurrentLog . severe ( mes , " Application was never started or root path wrong. " ) ;
System . exit ( - 1 ) ;
}
final Properties config = new Properties ( ) ;
FileInputStream fis = null ;
2021-01-24 22:50:37 +01:00
try {
fis = new FileInputStream ( new File ( homePath , " DATA/SETTINGS/yacy.conf " ) ) ;
2013-09-22 02:21:24 +02:00
config . load ( fis ) ;
} catch ( final FileNotFoundException e ) {
ConcurrentLog . severe ( mes , " could not find configuration file. " ) ;
System . exit ( - 1 ) ;
} catch ( final IOException e ) {
ConcurrentLog . severe ( mes , " could not read configuration file. " ) ;
System . exit ( - 1 ) ;
} finally {
2021-01-24 22:50:37 +01:00
if ( fis ! = null ) {
try {
fis . close ( ) ;
} catch ( final IOException e ) {
ConcurrentLog . logException ( e ) ;
}
}
2013-09-22 02:21:24 +02:00
}
return config ;
}
/ * *
2022-10-04 15:31:47 +02:00
* Call the shutdown - page of YaCy to tell it to shut down . This method is
* called if you start yacy with the argument - shutdown .
*
* @param homePath Root - path where all the information is to be found .
* /
2013-09-22 02:21:24 +02:00
public static void shutdown ( final File homePath ) {
// start up
System . out . println ( copyright ) ;
System . out . println ( hline ) ;
2021-10-24 20:49:59 +02:00
final LinkedHashMap < String , ContentBody > post = new LinkedHashMap < > ( ) ;
2017-03-26 11:48:00 +02:00
post . put ( " shutdown " , UTF8 . StringBody ( " " ) ) ;
submitPostURL ( homePath , " Steering.html " , " Terminate YaCy " , post ) ;
2013-09-22 02:21:24 +02:00
}
public static void update ( final File homePath ) {
// start up
System . out . println ( copyright ) ;
System . out . println ( hline ) ;
submitURL ( homePath , " ConfigUpdate_p.html?autoUpdate= " , " Update YaCy to most recent version " ) ;
}
2017-03-26 11:48:00 +02:00
/ * *
* Submits post data to the local peer URL , authenticating as administrator
* @param homePath directory containing YaCy DATA folder
* @param path url relative path part
* @param processdescription description of the operation for logging purpose
* @param post data to post
* /
private static void submitPostURL ( final File homePath , final String path , final String processdescription , final Map < String , ContentBody > post ) {
final Properties config = configuration ( " COMMAND-STEERING " , homePath ) ;
// read port
final int port = Integer . parseInt ( config . getProperty ( SwitchboardConstants . SERVER_PORT , " 8090 " ) ) ;
// read password
final String encodedPassword = config . getProperty ( SwitchboardConstants . ADMIN_ACCOUNT_B64MD5 , " " ) ;
final String adminUser = config . getProperty ( SwitchboardConstants . ADMIN_ACCOUNT_USER_NAME , " admin " ) ;
// send 'wget' to web interface
2021-10-31 18:47:10 +01:00
try ( final HTTPClient con = new HTTPClient ( ClientIdentification . yacyInternetCrawlerAgent ) ) {
2021-01-24 22:50:37 +01:00
/* First get a valid transaction token using HTTP GET */
2017-03-26 11:48:00 +02:00
con . GETbytes ( " http://localhost: " + port + " / " + path , adminUser , encodedPassword , false ) ;
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
if ( con . getStatusCode ( ) ! = HttpStatus . SC_OK ) {
throw new IOException ( " Error response from YACY socket: " + con . getHttpResponse ( ) . getStatusLine ( ) ) ;
}
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
final Header transactionTokenHeader = con . getHttpResponse ( ) . getFirstHeader ( HeaderFramework . X_YACY_TRANSACTION_TOKEN ) ;
if ( transactionTokenHeader = = null ) {
2021-01-24 22:50:37 +01:00
throw new IOException ( " Could not retrieve a valid transaction token " ) ;
2017-03-26 11:48:00 +02:00
}
/* Then POST the request */
post . put ( TransactionManager . TRANSACTION_TOKEN_PARAM , UTF8 . StringBody ( transactionTokenHeader . getValue ( ) ) ) ;
con . POSTbytes ( new MultiProtocolURL ( " http://localhost: " + port + " / " + path ) , null , post , adminUser , encodedPassword , false , false ) ;
if ( con . getStatusCode ( ) > = HttpStatus . SC_OK & & con . getStatusCode ( ) < HttpStatus . SC_MULTIPLE_CHOICES ) {
ConcurrentLog . config ( " COMMAND-STEERING " , " YACY accepted steering command: " + processdescription ) ;
} else {
2021-01-24 22:50:37 +01:00
ConcurrentLog . severe ( " COMMAND-STEERING " , " error response from YACY socket: " + con . getHttpResponse ( ) . getStatusLine ( ) ) ;
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
try {
2021-01-24 22:50:37 +01:00
HTTPClient . closeConnectionManager ( ) ;
} catch ( final InterruptedException e1 ) {
e1 . printStackTrace ( ) ;
}
2021-10-24 20:49:59 +02:00
2018-05-29 09:24:21 +02:00
RemoteInstance . closeConnectionManager ( ) ;
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
System . exit ( - 1 ) ;
}
} catch ( final IOException e ) {
ConcurrentLog . severe ( " COMMAND-STEERING " , " could not establish connection to YACY socket: " + e . getMessage ( ) ) ;
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
try {
2021-01-24 22:50:37 +01:00
HTTPClient . closeConnectionManager ( ) ;
} catch ( final InterruptedException e1 ) {
e1 . printStackTrace ( ) ;
}
2018-05-29 09:24:21 +02:00
RemoteInstance . closeConnectionManager ( ) ;
2021-10-24 20:49:59 +02:00
2017-03-26 11:48:00 +02:00
System . exit ( - 1 ) ;
}
try {
2021-01-24 22:50:37 +01:00
HTTPClient . closeConnectionManager ( ) ;
} catch ( final InterruptedException e ) {
e . printStackTrace ( ) ;
}
2018-05-29 09:24:21 +02:00
RemoteInstance . closeConnectionManager ( ) ;
2017-03-26 11:48:00 +02:00
// finished
ConcurrentLog . config ( " COMMAND-STEERING " , " SUCCESSFULLY FINISHED COMMAND: " + processdescription ) ;
}
2021-10-24 20:49:59 +02:00
2013-09-22 02:21:24 +02:00
private static void submitURL ( final File homePath , final String path , final String processdescription ) {
final Properties config = configuration ( " COMMAND-STEERING " , homePath ) ;
// read port
2017-03-18 20:02:26 +01:00
final int port = Integer . parseInt ( config . getProperty ( SwitchboardConstants . SERVER_PORT , " 8090 " ) ) ;
2013-09-22 02:21:24 +02:00
// read password
String encodedPassword = ( String ) config . get ( SwitchboardConstants . ADMIN_ACCOUNT_B64MD5 ) ;
if ( encodedPassword = = null ) encodedPassword = " " ; // not defined
// send 'wget' to web interface
2021-10-31 18:47:10 +01:00
try ( final HTTPClient con = new HTTPClient ( ClientIdentification . yacyInternetCrawlerAgent ) ) {
2014-01-19 15:21:23 +01:00
con . GETbytes ( " http://localhost: " + port + " / " + path , config . getProperty ( SwitchboardConstants . ADMIN_ACCOUNT_USER_NAME , " admin " ) , encodedPassword , false ) ;
2013-09-22 02:21:24 +02:00
if ( con . getStatusCode ( ) > 199 & & con . getStatusCode ( ) < 300 ) {
ConcurrentLog . config ( " COMMAND-STEERING " , " YACY accepted steering command: " + processdescription ) ;
2014-01-06 01:48:54 +01:00
2013-09-22 02:21:24 +02:00
} else {
2021-01-24 22:50:37 +01:00
ConcurrentLog . severe ( " COMMAND-STEERING " , " error response from YACY socket: " + con . getHttpResponse ( ) . getStatusLine ( ) ) ;
2013-09-22 02:21:24 +02:00
System . exit ( - 1 ) ;
}
} catch ( final IOException e ) {
ConcurrentLog . severe ( " COMMAND-STEERING " , " could not establish connection to YACY socket: " + e . getMessage ( ) ) ;
System . exit ( - 1 ) ;
}
try {
2021-01-24 22:50:37 +01:00
HTTPClient . closeConnectionManager ( ) ;
} catch ( final InterruptedException e ) {
e . printStackTrace ( ) ;
}
2018-05-29 09:24:21 +02:00
RemoteInstance . closeConnectionManager ( ) ;
2013-09-22 02:21:24 +02:00
// finished
ConcurrentLog . config ( " COMMAND-STEERING " , " SUCCESSFULLY FINISHED COMMAND: " + processdescription ) ;
}
2021-10-24 20:49:59 +02:00
2014-01-06 07:00:16 +01:00
/ * *
* read saved config file and perform action which need to be done before main task starts
* - like check on system alrady running etc .
2021-10-24 20:49:59 +02:00
*
2014-01-06 07:00:16 +01:00
* @param dataHome data directory
* /
2022-09-30 17:42:21 +02:00
static private void preReadSavedConfigandInit ( final File dataHome ) {
2021-10-24 20:49:59 +02:00
final File lockFile = new File ( dataHome , " DATA/yacy.running " ) ;
2014-01-06 07:00:16 +01:00
final String conf = " DATA/SETTINGS/yacy.conf " ;
2021-10-24 20:49:59 +02:00
2014-01-06 07:00:16 +01:00
// If YaCy is actually running, then we check if the server port is open.
// If yes, then we consider that a restart is a user mistake and then we just respond
// as the user expects and tell the browser to open the start page.
// That will especially happen if Windows Users double-Click the YaCy Icon on the desktop to simply
// open the web interface. (They don't think of 'servers' they just want to get to the search page).
// We need to parse the configuration file for that to get the host port
2021-10-24 20:49:59 +02:00
final File configFile = new File ( dataHome , conf ) ;
2014-01-06 07:00:16 +01:00
if ( configFile . exists ( ) ) {
2021-10-24 20:49:59 +02:00
final Properties p = new Properties ( ) ;
2017-06-02 12:14:29 +02:00
FileInputStream fis = null ;
2014-01-06 07:00:16 +01:00
try {
2017-06-02 12:14:29 +02:00
fis = new FileInputStream ( configFile ) ;
2014-03-24 02:32:17 +01:00
p . load ( fis ) ;
2021-10-24 20:49:59 +02:00
2014-01-06 11:28:05 +01:00
// test for yacy already running
if ( lockFile . exists ( ) ) { // another instance running? VM crash? User will have to care about this
//standard log system not up yet - use simply stdout
// prevents also creation of a log file while just opening browser
System . out . println ( " WARNING: the file " + lockFile + " exists, this usually means that a YaCy instance is still running. If you want to restart YaCy, try first ./stopYACY.sh, then ./startYACY.sh. If ./stopYACY.sh fails, try ./killYACY.sh " ) ;
2021-10-24 20:49:59 +02:00
final int port = Integer . parseInt ( p . getProperty ( SwitchboardConstants . SERVER_PORT , " 8090 " ) ) ;
2014-09-16 14:53:30 +02:00
if ( TimeoutRequest . ping ( " 127.0.0.1 " , port , 1000 ) ) {
Browser . openBrowser ( " http://localhost: " + port + " / " + p . getProperty ( SwitchboardConstants . BROWSER_POP_UP_PAGE , " index.html " ) ) ;
// Thats it; YaCy was running, the user is happy, we can stop now.
System . out . println ( " WARNING: YaCy instance was still running; just opening the browser and exit. " ) ;
System . exit ( 0 ) ;
} else {
// YaCy is not running; thus delete the file an go on as nothing was wrong.
System . err . println ( " INFO: delete old yacy.running file; likely previous YaCy session was not orderly shutdown! " ) ;
delete ( lockFile ) ;
2021-10-24 20:49:59 +02:00
}
2014-01-06 07:00:16 +01:00
}
2021-10-24 20:49:59 +02:00
} catch ( final IOException ex ) {
2014-09-16 23:14:13 +02:00
System . err . println ( " ERROR: config file seems to be corrupt " ) ;
System . err . println ( " ERROR: if problem persists, delete file " ) ;
System . err . println ( configFile . getAbsolutePath ( ) ) ;
2014-09-16 14:53:30 +02:00
ConcurrentLog . logException ( ex ) ;
2014-09-16 23:14:13 +02:00
ConcurrentLog . severe ( " Startup " , " cannot read " + configFile . toString ( ) + " , please delete the corrupted file if problem persits " ) ;
2017-06-02 12:14:29 +02:00
} finally {
2021-01-24 22:50:37 +01:00
try {
fis . close ( ) ;
2021-10-24 20:49:59 +02:00
} catch ( final IOException e ) {
2021-01-24 22:50:37 +01:00
ConcurrentLog . warn ( " Startup " , " Could not close file " + configFile ) ;
}
2014-09-16 14:53:30 +02:00
}
2014-01-06 07:00:16 +01:00
}
2021-10-24 20:49:59 +02:00
}
2013-09-22 02:21:24 +02:00
/ * *
* Main - method which is started by java . Checks for special arguments or
* starts up the application .
*
* @param args
* Given arguments from the command line .
* /
2022-09-30 17:42:21 +02:00
public static void main ( final String args [ ] ) {
2013-09-22 02:21:24 +02:00
2021-01-24 22:50:37 +01:00
try {
// check assertion status
//ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
boolean assertionenabled = false ;
assert ( assertionenabled = true ) = = true ; // compare to true to remove warning: "Possible accidental assignement"
if ( assertionenabled ) System . out . println ( " Asserts are enabled " ) ;
// check memory amount
System . gc ( ) ;
final long startupMemFree = MemoryControl . free ( ) ;
final long startupMemTotal = MemoryControl . total ( ) ;
// maybe go into headless awt mode: we have three cases depending on OS and one exception:
// windows : better do not go into headless mode
// mac : go into headless mode because an application is shown in gui which may not be wanted
// linux : go into headless mode because this does not need any head operation
// exception : if the -gui option is used then do not go into headless mode since that uses a gui
boolean headless = true ;
if ( OS . isWindows ) headless = false ;
if ( args . length > = 1 & & args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -gui " ) ) headless = false ;
System . setProperty ( " java.awt.headless " , headless ? " true " : " false " ) ;
2021-10-24 20:49:59 +02:00
final StringBuilder s = new StringBuilder ( ) ; for ( final String a : args ) s . append ( a ) . append ( " " ) ;
yacyRelease . startParameter = s . toString ( ) . trim ( ) ;
2021-01-24 22:50:37 +01:00
2021-10-24 20:49:59 +02:00
// case for the application path if started normally with a jre command
2021-01-24 22:50:37 +01:00
File applicationRoot = new File ( System . getProperty ( " user.dir " ) . replace ( '\\' , '/' ) ) ;
2021-10-24 20:49:59 +02:00
// try to find the application root path within a Mac application
// call com.apple.eio.FileManager.getPathToApplicationBundle();
2022-01-18 13:02:12 +01:00
if ( ! OS . isWindows ) { // prevent that YaCy always starts with an exception message on none Apple systems
try {
// these methods will cause a warning: remove them with --illegal-access=permit
final Class < ? > comAppleEioFileManagerClass = Class . forName ( " com.apple.eio.FileManager " ) ;
final Method getPathToApplicationBundleMethod = ClassProvider . getStaticMethod ( comAppleEioFileManagerClass , " getPathToApplicationBundle " , null ) ;
final String apppath = ( String ) getPathToApplicationBundleMethod . invoke ( null ) ;
System . out . println ( " PathToApplicationBundle = " + apppath ) ; // PathToApplicationBundle = /Users/admin/git/rc1/build/YaCy.app
if ( apppath ! = null & & apppath . endsWith ( " .app " ) ) {
// modify the applicationRoot path to within the app file
applicationRoot = new File ( apppath + " /Contents " ) ;
System . setProperty ( " user.dir " , applicationRoot . getAbsolutePath ( ) ) ; // required since nasty code elswhere is using it
System . setProperty ( " user.home " , applicationRoot . getAbsolutePath ( ) ) ; // well
}
} catch ( ClassNotFoundException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e ) {
e . printStackTrace ( ) ;
2021-10-25 18:37:39 +02:00
}
2021-10-24 20:49:59 +02:00
}
2021-01-24 22:50:37 +01:00
File dataRoot = applicationRoot ;
//System.out.println("args.length=" + args.length);
//System.out.print("args=["); for (int i = 0; i < args.length; i++) System.out.print(args[i] + ", "); System.out.println("]");
if ( ( args . length > = 1 ) & & ( args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -startup " ) | | args [ 0 ] . equals ( " -start " ) ) ) {
// normal start-up of yacy
if ( args . length > 1 ) {
dataRoot = new File ( args [ 1 ] ) ;
if ( ! dataRoot . isAbsolute ( ) ) {
/* data root folder provided as a path relative to the user home folder */
dataRoot = new File ( System . getProperty ( " user.home " ) . replace ( '\\' , '/' ) , args [ 1 ] ) ;
}
}
2016-09-02 11:23:02 +02:00
preReadSavedConfigandInit ( dataRoot ) ;
2021-01-24 22:50:37 +01:00
startup ( dataRoot , applicationRoot , startupMemFree , startupMemTotal , false ) ;
} else if ( args . length > = 1 & & args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -gui " ) ) {
// start-up of yacy with gui
if ( args . length > 1 ) {
dataRoot = new File ( args [ 1 ] ) ;
if ( ! dataRoot . isAbsolute ( ) ) {
/* data root folder provided as a path relative to the user home folder */
dataRoot = new File ( System . getProperty ( " user.home " ) . replace ( '\\' , '/' ) , args [ 1 ] ) ;
}
}
2016-09-02 11:23:02 +02:00
preReadSavedConfigandInit ( dataRoot ) ;
2021-01-24 22:50:37 +01:00
startup ( dataRoot , applicationRoot , startupMemFree , startupMemTotal , true ) ;
} else if ( ( args . length > = 1 ) & & ( ( args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -shutdown " ) ) | | ( args [ 0 ] . equals ( " -stop " ) ) ) ) {
// normal shutdown of yacy
if ( args . length = = 2 ) applicationRoot = new File ( args [ 1 ] ) ;
shutdown ( applicationRoot ) ;
} else if ( ( args . length > = 1 ) & & ( args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -update " ) ) ) {
// aut-update yacy
if ( args . length = = 2 ) applicationRoot = new File ( args [ 1 ] ) ;
update ( applicationRoot ) ;
} else if ( ( args . length > = 1 ) & & ( args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -version " ) ) ) {
// show yacy version
System . out . println ( copyright ) ;
2017-06-27 06:42:33 +02:00
} else if ( ( args . length > 1 ) & & ( args [ 0 ] . toLowerCase ( Locale . ROOT ) . equals ( " -config " ) ) ) {
2022-10-04 15:31:47 +02:00
// set config parameter. Special handling of adminAccount=user:pwd (generates md5 encoded password)
// on Windows parameter should be enclosed in doublequotes to accept = sign (e.g. -config "port=8090" "port.ssl=8043")
final File f = new File ( dataRoot , " DATA/SETTINGS/ " ) ;
if ( ! f . exists ( ) ) {
mkdirsIfNeseccary ( f ) ;
} else {
if ( new File ( dataRoot , " DATA/yacy.running " ) . exists ( ) ) {
System . out . println ( " please restart YaCy " ) ;
2014-02-24 05:16:31 +01:00
}
2022-10-04 15:31:47 +02:00
}
// use serverSwitch to read config properties (including init values from yacy.init
final serverSwitch ss = new serverSwitch ( dataRoot , applicationRoot , " defaults/yacy.init " , " DATA/SETTINGS/yacy.conf " ) ;
for ( int icnt = 1 ; icnt < args . length ; icnt + + ) {
final String cfg = args [ icnt ] ;
final int pos = cfg . indexOf ( '=' ) ;
if ( pos > 0 ) {
final String cmd = cfg . substring ( 0 , pos ) ;
final String val = cfg . substring ( pos + 1 ) ;
if ( ! val . isEmpty ( ) ) {
if ( cmd . equalsIgnoreCase ( SwitchboardConstants . ADMIN_ACCOUNT ) ) { // special command to set adminusername and md5-pwd
final int cpos = val . indexOf ( ':' ) ; //format adminAccount=adminname:adminpwd
if ( cpos > = 0 ) {
String username = val . substring ( 0 , cpos ) ;
final String pwdtxt = val . substring ( cpos + 1 ) ;
if ( ! username . isEmpty ( ) ) {
ss . setConfig ( SwitchboardConstants . ADMIN_ACCOUNT_USER_NAME , username ) ;
System . out . println ( " Set property " + SwitchboardConstants . ADMIN_ACCOUNT_USER_NAME + " = " + username ) ;
} else {
username = ss . getConfig ( SwitchboardConstants . ADMIN_ACCOUNT_USER_NAME , " admin " ) ;
2014-02-24 05:16:31 +01:00
}
2022-10-04 15:31:47 +02:00
ss . setConfig ( SwitchboardConstants . ADMIN_ACCOUNT_B64MD5 , sb . encodeDigestAuth ( username , pwdtxt ) ) ;
System . out . println ( " Set property " + SwitchboardConstants . ADMIN_ACCOUNT_B64MD5 + " = " + ss . getConfig ( SwitchboardConstants . ADMIN_ACCOUNT_B64MD5 , " " ) ) ;
2014-02-24 05:16:31 +01:00
}
2022-10-04 15:31:47 +02:00
} else {
ss . setConfig ( cmd , val ) ;
System . out . println ( " Set property " + cmd + " = " + val ) ;
2014-02-24 05:16:31 +01:00
}
}
2022-10-04 15:31:47 +02:00
} else {
System . out . println ( " skip parameter " + cfg + " (equal sign missing, put parameter in doublequotes) " ) ;
2014-02-24 05:16:31 +01:00
}
2022-10-04 15:31:47 +02:00
System . out . println ( ) ;
}
2016-09-02 11:23:02 +02:00
} else {
2021-01-24 22:50:37 +01:00
if ( args . length = = 1 ) {
applicationRoot = new File ( args [ 0 ] ) ;
}
2016-09-02 11:23:02 +02:00
preReadSavedConfigandInit ( dataRoot ) ;
2021-01-24 22:50:37 +01:00
startup ( dataRoot , applicationRoot , startupMemFree , startupMemTotal , false ) ;
}
} finally {
ConcurrentLog . shutdown ( ) ;
}
2013-09-22 02:21:24 +02:00
}
}
/ * *
2022-10-04 15:31:47 +02:00
* This class is a helper class whose instance is started , when the java virtual
* machine shuts down . Signals the plasmaSwitchboard to shut down .
* /
2013-09-22 02:21:24 +02:00
class shutdownHookThread extends Thread {
private final Switchboard sb ;
2016-12-28 09:47:27 +01:00
private final Semaphore shutdownSemaphore ;
2013-09-22 02:21:24 +02:00
2016-12-28 09:47:27 +01:00
public shutdownHookThread ( final Switchboard sb , final Semaphore shutdownSemaphore ) {
2016-10-22 17:17:21 +02:00
super ( " yacy.shutdownHookThread " ) ;
2013-09-22 02:21:24 +02:00
this . sb = sb ;
2016-12-28 09:47:27 +01:00
this . shutdownSemaphore = shutdownSemaphore ;
2013-09-22 02:21:24 +02:00
}
@Override
public void run ( ) {
try {
if ( ! this . sb . isTerminated ( ) ) {
ConcurrentLog . config ( " SHUTDOWN " , " Shutdown via shutdown hook. " ) ;
2016-12-28 09:47:27 +01:00
/ * Print also to the standard output , as the LogManager is likely to have
* been concurrently reset by its own shutdown hook thread * /
System . out . println ( " SHUTDOWN Starting shutdown via shutdown hook. " ) ;
2013-09-22 02:21:24 +02:00
// sending the yacy main thread a shutdown signal
ConcurrentLog . fine ( " SHUTDOWN " , " Signaling shutdown to the switchboard. " ) ;
this . sb . terminate ( " shutdown hook " ) ;
// waiting for the yacy thread to finish execution
ConcurrentLog . fine ( " SHUTDOWN " , " Waiting for main thread to finish. " ) ;
2021-10-24 20:49:59 +02:00
2016-12-28 09:47:27 +01:00
/ * Main thread will release the shutdownSemaphore once completely terminated .
* We do not wait indefinitely as the application is supposed here to quickly terminate * /
2018-11-15 17:32:22 +01:00
final int maxWaitTime = 30 ;
if ( ! this . shutdownSemaphore . tryAcquire ( maxWaitTime , TimeUnit . SECONDS ) ) {
System . out . println ( " Shutting down JVM. Main thread did not completely finish within " + maxWaitTime + " seconds. " ) ;
}
2013-09-22 02:21:24 +02:00
}
} catch ( final Exception e ) {
ConcurrentLog . severe ( " SHUTDOWN " , " Unexpected error. " + e . getClass ( ) . getName ( ) , e ) ;
}
}
2022-07-10 11:13:34 +02:00
}