merge of '380c87670c1c931cf39e93d5600c4954c6e13d1e'

and '4fe47402bea065caae229256d58d87e60607602a'
This commit is contained in:
zab2
2015-11-04 22:22:58 +00:00
371 changed files with 10541 additions and 6971 deletions

View File

@@ -18,7 +18,7 @@ public class CoreVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = "0.9.21";
public final static String VERSION = "0.9.22";
/**
* For Vuze.

View File

@@ -84,6 +84,7 @@ public class I2PAppContext {
private RandomSource _random;
private KeyGenerator _keyGenerator;
protected KeyRing _keyRing; // overridden in RouterContext
@SuppressWarnings("deprecation")
private SimpleScheduler _simpleScheduler;
private SimpleTimer _simpleTimer;
private SimpleTimer2 _simpleTimer2;
@@ -532,7 +533,7 @@ public class I2PAppContext {
* @return set of Strings containing the names of defined system properties
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Set<String> getPropertyNames() {
public Set<String> getPropertyNames() {
// clone to avoid ConcurrentModificationException
Set<String> names = new HashSet<String>((Set<String>) (Set) ((Properties) System.getProperties().clone()).keySet()); // TODO-Java6: s/keySet()/stringPropertyNames()/
if (_overrideProps != null)
@@ -940,6 +941,7 @@ public class I2PAppContext {
* @since 0.9 to replace static instance in the class
* @deprecated in 0.9.20, use simpleTimer2()
*/
@SuppressWarnings("deprecation")
public SimpleScheduler simpleScheduler() {
if (!_simpleSchedulerInitialized)
initializeSimpleScheduler();

View File

@@ -18,6 +18,7 @@ import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.SigningPrivateKey;
/**
@@ -98,7 +99,7 @@ public interface I2PSession {
* objects that were sent along side the given keyUsed.
* @return success
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
@@ -106,7 +107,7 @@ public interface I2PSession {
* @param tagsSent UNUSED, IGNORED.
* @return success
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
@@ -116,7 +117,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire) throws I2PSessionException;
/**
* See I2PSessionMuxedImpl for proto/port details.
@@ -133,7 +134,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromPort, int toPort) throws I2PSessionException;
/**
@@ -152,7 +153,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromPort, int toPort) throws I2PSessionException;
/**
@@ -171,7 +172,7 @@ public interface I2PSession {
* @return success
* @since 0.8.4
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromPort, int toPort, int flags) throws I2PSessionException;
/**

View File

@@ -132,7 +132,7 @@ class I2CPMessageProducer {
* @param newKey unused - no end-to-end crypto
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
SessionKey key, Set<SessionTag> tags, SessionKey newKey, long expires) throws I2PSessionException {
sendMessage(session, dest, nonce, payload, expires, 0);
}

View File

@@ -29,6 +29,7 @@ import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Log;
@@ -210,17 +211,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromport, int toport) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromport, int toport) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromport, int toport, int flags) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
@@ -253,7 +254,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param tagsSent unused - no end-to-end crypto
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException {
return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0);
}
@@ -261,7 +262,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0);
}
@@ -272,7 +273,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
synchronized (_stateLock) {
@@ -339,7 +340,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
return sendBestEffort(dest, payload, expires, 0);
}

View File

@@ -19,6 +19,7 @@ import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.util.Log;
@@ -163,7 +164,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires)
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED);
}
@@ -173,7 +174,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
* @param tagsSent unused - no end-to-end crypto
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromport, int toport) throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromport, toport);
}
@@ -192,7 +193,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires,
int proto, int fromPort, int toPort)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromPort, toPort, 0);
@@ -213,7 +214,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires,
int proto, int fromPort, int toPort, int flags)
throws I2PSessionException {
payload = prepPayload(payload, offset, size, proto, fromPort, toPort);

View File

@@ -621,11 +621,33 @@ public class BlockfileNamingService extends DummyNamingService {
////////// Start NamingService API
/*
*
* Will strip a "www." prefix and retry if lookup fails
*
* @param hostname upper/lower case ok
* @param options If non-null and contains the key "list", lookup in
* that list only, otherwise all lists
*/
@Override
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
Destination rv = lookup2(hostname, lookupOptions, storedOptions);
if (rv == null) {
// if hostname starts with "www.", strip and try again
// but not for www.i2p
hostname = hostname.toLowerCase(Locale.US);
if (hostname.startsWith("www.") && hostname.length() > 7) {
hostname = hostname.substring(4);
rv = lookup2(hostname, lookupOptions, storedOptions);
}
}
return rv;
}
/*
* @param options If non-null and contains the key "list", lookup in
* that list only, otherwise all lists
*/
private Destination lookup2(String hostname, Properties lookupOptions, Properties storedOptions) {
String listname = null;
if (lookupOptions != null)
listname = lookupOptions.getProperty("list");

View File

@@ -41,8 +41,8 @@ public class MetaNamingService extends DummyNamingService {
while (tok.hasMoreTokens()) {
try {
Class<?> cls = Class.forName(tok.nextToken());
Constructor<?> con = cls.getConstructor(new Class[] { I2PAppContext.class });
addNamingService((NamingService)con.newInstance(new Object[] { context }), false);
Constructor<?> con = cls.getConstructor(I2PAppContext.class);
addNamingService((NamingService)con.newInstance(), false);
} catch (Exception ex) {
}
}

View File

@@ -536,8 +536,8 @@ public abstract class NamingService {
String impl = context.getProperty(PROP_IMPL, DEFAULT_IMPL);
try {
Class<?> cls = Class.forName(impl);
Constructor<?> con = cls.getConstructor(new Class[] { I2PAppContext.class });
instance = (NamingService)con.newInstance(new Object[] { context });
Constructor<?> con = cls.getConstructor(I2PAppContext.class);
instance = (NamingService)con.newInstance(context);
} catch (Exception ex) {
Log log = context.logManager().getLog(NamingService.class);
// Blockfile may throw RuntimeException but HostsTxt won't

View File

@@ -77,6 +77,8 @@ public class SingleFileNamingService extends NamingService {
}
/**
* Will strip a "www." prefix and retry if lookup fails
*
* @param hostname case-sensitive; caller should convert to lower case
* @param lookupOptions ignored
* @param storedOptions ignored
@@ -85,6 +87,8 @@ public class SingleFileNamingService extends NamingService {
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
try {
String key = getKey(hostname);
if (key == null && hostname.startsWith("www.") && hostname.length() > 7)
key = getKey(hostname.substring(4));
if (key != null)
return lookupBase64(key);
} catch (Exception ioe) {

View File

@@ -44,28 +44,7 @@ public class CryptixAESEngine extends AESEngine {
/** see test results below */
private static final int MIN_SYSTEM_AES_LENGTH = 704;
private static final boolean USE_SYSTEM_AES;
static {
boolean systemOK = false;
if (hasAESNI()) {
try {
systemOK = Cipher.getMaxAllowedKeyLength("AES") >= 256;
} catch (GeneralSecurityException gse) {
// a NoSuchAlgorithmException
} catch (NoSuchMethodError nsme) {
// JamVM, gij
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
systemOK = true;
} catch (GeneralSecurityException gse) {
}
}
}
USE_SYSTEM_AES = systemOK;
//System.out.println("Using system AES? " + systemOK);
}
private static final boolean USE_SYSTEM_AES = hasAESNI() && CryptoCheck.isUnlimited();
/**
* Do we have AES-NI support in the processor and JVM?

View File

@@ -0,0 +1,47 @@
package net.i2p.crypto;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* Moved from CryptixAESEngine and net.i2p.router.tasks.CryptoChecker
*
* @since 0.9.23
*/
public class CryptoCheck {
private static final boolean _isUnlimited;
static {
boolean unlimited = false;
try {
unlimited = Cipher.getMaxAllowedKeyLength("AES") >= 256;
} catch (GeneralSecurityException gse) {
// a NoSuchAlgorithmException
} catch (NoSuchMethodError nsme) {
// JamVM, gij
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
unlimited = true;
} catch (GeneralSecurityException gse) {
}
}
_isUnlimited = unlimited;
}
private CryptoCheck() {}
/**
* Do we have unlimited crypto?
*/
public static boolean isUnlimited() {
return _isUnlimited;
}
public static void main(String args[]) {
System.out.println("Unlimited? " + isUnlimited());
}
}

View File

@@ -92,8 +92,8 @@ public class CryptoConstants {
if (ECConstants.isBCAvailable()) {
try {
Class<?> cls = Class.forName("org.bouncycastle.jce.spec.ElGamalParameterSpec");
Constructor<?> con = cls.getConstructor(new Class[] {BigInteger.class, BigInteger.class});
spec = (AlgorithmParameterSpec)con.newInstance(new Object[] {elgp, elgg});
Constructor<?> con = cls.getConstructor(BigInteger.class, BigInteger.class);
spec = (AlgorithmParameterSpec)con.newInstance(elgp, elgg);
//System.out.println("BC ElG spec loaded");
} catch (Exception e) {
//System.out.println("BC ElG spec failed");

View File

@@ -42,8 +42,8 @@ class ECConstants {
if (Security.getProvider("BC") == null) {
try {
Class<?> cls = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
Constructor<?> con = cls.getConstructor(new Class[0]);
Provider bc = (Provider)con.newInstance(new Object[0]);
Constructor<?> con = cls.getConstructor();
Provider bc = (Provider)con.newInstance();
Security.addProvider(bc);
log("Added BC provider");
loaded = true;

View File

@@ -501,6 +501,7 @@ public class KeyStoreUtil {
l.log(level, msg, t);
}
/****
public static void main(String[] args) {
try {
if (args.length > 0) {
@@ -521,4 +522,5 @@ public class KeyStoreUtil {
e.printStackTrace();
}
}
****/
}

View File

@@ -11,7 +11,9 @@ import java.util.Map;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.data.Hash;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.SystemVersion;
/**
* Defines the properties for various signature types
@@ -193,8 +195,24 @@ public enum SigType {
return true;
try {
getParams();
if (getBaseAlgorithm() != SigAlgo.EdDSA)
Signature.getInstance(getAlgorithmName());
if (getBaseAlgorithm() != SigAlgo.EdDSA) {
Signature jsig = Signature.getInstance(getAlgorithmName());
if (getBaseAlgorithm() == SigAlgo.EC && SystemVersion.isGentoo() ) {
// Do a full keygen/sign test on Gentoo, because it lies. Keygen works but sigs fail.
// https://bugs.gentoo.org/show_bug.cgi?id=528338
// http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=2497
// http://zzz.i2p/topics/1931
// Be sure nothing in the code paths below calls isAvailable()
// get an I2P keypair
SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(this);
SigningPrivateKey privKey = (SigningPrivateKey) keys[1];
// convert privkey back to Java key and sign
jsig.initSign(SigUtil.toJavaECKey(privKey));
// use the pubkey as random data
jsig.update(keys[0].getData());
jsig.sign();
}
}
getDigestInstance();
getHashInstance();
} catch (Exception e) {

View File

@@ -16,7 +16,7 @@ import java.io.Serializable;
* maps, and the like.
*
*/
public class ByteArray implements Serializable, Comparable {
public class ByteArray implements Serializable, Comparable<ByteArray> {
private byte[] _data;
private int _valid;
private int _offset;
@@ -85,9 +85,8 @@ public class ByteArray implements Serializable, Comparable {
return (llen == rlen) && DataHelper.eq(lhs, loff, rhs, roff, llen);
}
public final int compareTo(Object obj) {
if (obj.getClass() != getClass()) throw new ClassCastException("invalid object: " + obj);
return DataHelper.compareTo(_data, ((ByteArray)obj).getData());
public final int compareTo(ByteArray ba) {
return DataHelper.compareTo(_data, ba.getData());
}
@Override

View File

@@ -47,16 +47,17 @@ public class Certificate extends DataStructureImpl {
public final static int CERTIFICATE_TYPE_KEY = 5;
/**
* If null cert, return immutable static instance, else create new
* If null, P256 key, or Ed25519 key cert, return immutable static instance, else create new
* @throws DataFormatException if not enough bytes
* @since 0.8.3
*/
public static Certificate create(byte[] data, int off) throws DataFormatException {
int type;
byte[] payload;
int length;
try {
type = data[off] & 0xff;
int length = (int) DataHelper.fromLong(data, off + 1, 2);
length = (int) DataHelper.fromLong(data, off + 1, 2);
if (type == 0 && length == 0)
return NULL_CERT;
// from here down roughly the same as readBytes() below
@@ -68,6 +69,12 @@ public class Certificate extends DataStructureImpl {
throw new DataFormatException("not enough bytes", aioobe);
}
if (type == CERTIFICATE_TYPE_KEY) {
if (length == 4) {
if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD))
return KeyCertificate.ELG_Ed25519_CERT;
if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD))
return KeyCertificate.ELG_ECDSA256_CERT;
}
try {
return new KeyCertificate(payload);
} catch (DataFormatException dfe) {
@@ -78,7 +85,7 @@ public class Certificate extends DataStructureImpl {
}
/**
* If null cert, return immutable static instance, else create new
* If null, P256 key, or Ed25519 key cert, return immutable static instance, else create new
* @since 0.8.3
*/
public static Certificate create(InputStream in) throws DataFormatException, IOException {
@@ -93,8 +100,15 @@ public class Certificate extends DataStructureImpl {
int read = DataHelper.read(in, payload);
if (read != length)
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
if (type == CERTIFICATE_TYPE_KEY)
if (type == CERTIFICATE_TYPE_KEY) {
if (length == 4) {
if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD))
return KeyCertificate.ELG_Ed25519_CERT;
if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD))
return KeyCertificate.ELG_ECDSA256_CERT;
}
return new KeyCertificate(payload);
}
return new Certificate(type, payload);
}

View File

@@ -1546,7 +1546,7 @@ public class DataHelper {
// years
t = ngettext("1 year", "{0} years", (int) (ms / (365L * 24 * 60 * 60 * 1000)));
} else {
return _("n/a");
return _t("n/a");
}
// Replace minus sign to work around
// bug in Chrome (and IE?), line breaks at the minus sign
@@ -1593,7 +1593,7 @@ public class DataHelper {
// years
t = ngettext("1 year", "{0} years", (int) (ms / (365L * 24 * 60 * 60 * 1000)));
} else {
return _("n/a");
return _t("n/a");
}
if (ms < 0)
t = t.replace("-", "&minus;");
@@ -1602,7 +1602,7 @@ public class DataHelper {
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
private static String _(String key) {
private static String _t(String key) {
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}

View File

@@ -17,15 +17,41 @@ public class KeyCertificate extends Certificate {
public static final int HEADER_LENGTH = 4;
/** @since 0.9.22 pkg private for Certificate.create() */
static final byte[] Ed25519_PAYLOAD = new byte[] {
0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, 0
};
/** @since 0.9.22 pkg private for Certificate.create() */
static final byte[] ECDSA256_PAYLOAD = new byte[] {
0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
/**
* An immutable ElG/ECDSA-P256 certificate.
*/
public static final KeyCertificate ELG_ECDSA256_CERT;
/**
* An immutable ElG/Ed25519 certificate.
* @since 0.9.22
*/
public static final KeyCertificate ELG_Ed25519_CERT;
static {
KeyCertificate kc;
try {
kc = new ECDSA256Cert();
} catch (DataFormatException dfe) {
kc = null; // won't happen
throw new RuntimeException(dfe); // won't happen
}
ELG_ECDSA256_CERT = kc;
try {
kc = new Ed25519Cert();
} catch (DataFormatException dfe) {
throw new RuntimeException(dfe); // won't happen
}
ELG_Ed25519_CERT = kc;
}
/**
@@ -185,19 +211,17 @@ public class KeyCertificate extends Certificate {
/**
* An immutable ElG/ECDSA-256 certificate.
* @since 0.8.3
*/
private static final class ECDSA256Cert extends KeyCertificate {
private static final byte[] ECDSA256_DATA = new byte[] {
CERTIFICATE_TYPE_KEY, 0, HEADER_LENGTH, 0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
private static final int ECDSA256_LENGTH = ECDSA256_DATA.length;
private static final byte[] ECDSA256_PAYLOAD = new byte[] {
0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
private final int _hashcode;
public ECDSA256Cert() throws DataFormatException {
super(ECDSA256_PAYLOAD);
_hashcode = super.hashCode();
}
/** @throws RuntimeException always */
@@ -246,7 +270,75 @@ public class KeyCertificate extends Certificate {
/** Overridden for efficiency */
@Override
public int hashCode() {
return 1234567;
return _hashcode;
}
}
/**
* An immutable ElG/Ed25519 certificate.
* @since 0.9.22
*/
private static final class Ed25519Cert extends KeyCertificate {
private static final byte[] ED_DATA = new byte[] { CERTIFICATE_TYPE_KEY,
0, HEADER_LENGTH,
0, (byte) SigType.EdDSA_SHA512_Ed25519.getCode(),
0, 0
};
private static final int ED_LENGTH = ED_DATA.length;
private final int _hashcode;
public Ed25519Cert() throws DataFormatException {
super(Ed25519_PAYLOAD);
_hashcode = super.hashCode();
}
/** @throws RuntimeException always */
@Override
public void setCertificateType(int type) {
throw new RuntimeException("Data already set");
}
/** @throws RuntimeException always */
@Override
public void setPayload(byte[] payload) {
throw new RuntimeException("Data already set");
}
/** @throws RuntimeException always */
@Override
public void readBytes(InputStream in) throws DataFormatException, IOException {
throw new RuntimeException("Data already set");
}
/** Overridden for efficiency */
@Override
public void writeBytes(OutputStream out) throws IOException {
out.write(ED_DATA);
}
/** Overridden for efficiency */
@Override
public int writeBytes(byte target[], int offset) {
System.arraycopy(ED_DATA, 0, target, offset, ED_LENGTH);
return ED_LENGTH;
}
/** @throws RuntimeException always */
@Override
public int readBytes(byte source[], int offset) throws DataFormatException {
throw new RuntimeException("Data already set");
}
/** Overridden for efficiency */
@Override
public int size() {
return ED_LENGTH;
}
/** Overridden for efficiency */
@Override
public int hashCode() {
return _hashcode;
}
}
}

View File

@@ -46,7 +46,6 @@ import net.i2p.util.SystemVersion;
public class SDSCache<V extends SimpleDataStructure> {
//private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(SDSCache.class);
private static final Class[] conArg = new Class[] { byte[].class };
private static final double MIN_FACTOR = 0.20;
private static final double MAX_FACTOR = 5.0;
private static final double FACTOR;
@@ -74,7 +73,7 @@ public class SDSCache<V extends SimpleDataStructure> {
_cache = new LHMCache<Integer, WeakReference<V>>(size);
_datalen = len;
try {
_rvCon = rvClass.getConstructor(conArg);
_rvCon = rvClass.getConstructor(byte[].class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("SDSCache init error", e);
}

View File

@@ -628,6 +628,7 @@ public class KBucketSet<T extends SimpleDataStructure> {
* @param data size <= SDS length, else throws IAE
* Can be 1 bigger if top byte is zero
*/
@SuppressWarnings("unchecked")
private T makeKey(byte[] data) {
int len = _us.length();
int dlen = data.length;

View File

@@ -15,6 +15,8 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.http.conn.util.InetAddressUtils;
import net.i2p.I2PAppContext;
/**
@@ -229,10 +231,10 @@ public abstract class Addresses {
I2PAppContext ctx = I2PAppContext.getCurrentContext();
if (ctx != null && ctx.isRouterContext()) {
long maxMemory = SystemVersion.getMaxMemory();
long min = 128;
long min = 256;
long max = 4096;
// 512 nominal for 128 MB
size = (int) Math.max(min, Math.min(max, 1 + (maxMemory / (256*1024))));
// 1024 nominal for 128 MB
size = (int) Math.max(min, Math.min(max, 1 + (maxMemory / (128*1024))));
} else {
size = 32;
}
@@ -260,12 +262,9 @@ public abstract class Addresses {
}
if (rv == null) {
try {
boolean isIPv4 = host.replaceAll("[0-9\\.]", "").length() == 0;
if (isIPv4 && host.replaceAll("[0-9]", "").length() != 3)
return null;
rv = InetAddress.getByName(host).getAddress();
if (isIPv4 ||
host.replaceAll("[0-9a-fA-F:]", "").length() == 0) {
if (InetAddressUtils.isIPv4Address(host) ||
InetAddressUtils.isIPv6Address(host)) {
synchronized (_IPAddress) {
_IPAddress.put(host, rv);
}

View File

@@ -24,6 +24,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gnu.getopt.Getopt;
@@ -312,22 +314,52 @@ public class EepGet {
System.exit(1);
}
/**
* Parse URL for a viable filename.
*
* @param url a URL giving the location of an online resource
* @return a filename to save the resource as on local filesystem
*/
public static String suggestName(String url) {
int last = url.lastIndexOf('/');
if ((last < 0) || (url.lastIndexOf('#') > last))
last = url.lastIndexOf('#');
if ((last < 0) || (url.lastIndexOf('?') > last))
last = url.lastIndexOf('?');
if ((last < 0) || (url.lastIndexOf('=') > last))
last = url.lastIndexOf('=');
URL nameURL = null; // URL object
String name; // suggested name
String name = null;
if (last >= 0)
name = sanitize(url.substring(last+1));
if ( (name != null) && (name.length() > 0) )
return name;
else
return sanitize(url);
try {
nameURL = new URL(url);
} catch (MalformedURLException e) {
System.err.println("Please enter a properly formed URL.");
System.exit(1);
}
String path = nameURL.getPath(); // discard any URI queries
// if no file specified, eepget scrapes webpage - use domain as name
Pattern slashes = Pattern.compile("/+");
Matcher matcher = slashes.matcher(path);
// if empty path or just /'s - nameURL lets multiple /'s through
if (path.equals("") || matcher.matches()) {
name = sanitize(nameURL.getAuthority());
// if path specified
} else {
int last = path.lastIndexOf('/');
// if last / not at end of string, use following string as filename
if (last != path.length() - 1) {
name = sanitize(path.substring(last + 1));
// if there's a trailing / group look for previous / as trim point
} else {
int i = 1;
int slash;
while (true) {
slash = path.lastIndexOf('/', last - i);
if (slash != last - i) {
break;
}
i += 1;
}
name = sanitize(path.substring(slash + 1, path.length() - i));
}
}
return name;
}
@@ -755,6 +787,8 @@ public class EepGet {
Thread pusher = null;
_decompressException = null;
if (_isGzippedResponse) {
if (_log.shouldInfo())
_log.info("Gzipped response, starting decompressor");
PipedInputStream pi = BigPipedInputStream.getInstance();
PipedOutputStream po = new PipedOutputStream(pi);
pusher = new I2PAppThread(new Gunzipper(pi, _out), "EepGet Decompressor");
@@ -1096,7 +1130,7 @@ public class EepGet {
*/
private int handleStatus(String line) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Status line: [" + line + "]");
_log.debug("Status line: [" + line.trim() + "]");
String[] toks = line.split(" ", 3);
if (toks.length < 2) {
if (_log.shouldLog(Log.WARN))
@@ -1160,17 +1194,13 @@ public class EepGet {
lookahead[1] = lookahead[2];
lookahead[2] = (byte)cur;
}
private static boolean isEndOfHeaders(byte lookahead[]) {
byte first = lookahead[0];
byte second = lookahead[1];
byte third = lookahead[2];
return (isNL(second) && isNL(third)) || // \n\n
(isNL(first) && isNL(third)); // \n\r\n
return lookahead[2] == NL &&
(lookahead[0] == NL || lookahead[1] == NL); // \n\n or \n\r\n
}
/** we ignore any potential \r, since we trim it on write anyway */
private static final byte NL = '\n';
private static boolean isNL(byte b) { return (b == NL); }
/**
* @param timeout may be null
@@ -1315,7 +1345,8 @@ public class EepGet {
buf.append("Content-length: ").append(_postData.length()).append("\r\n");
// This will be replaced if we are going through I2PTunnelHTTPClient
buf.append("Accept-Encoding: ");
if ((!_shouldProxy) &&
// as of 0.9.23, the proxy passes the Accept-Encoding header through
if ( /* (!_shouldProxy) && */
// This is kindof a hack, but if we are downloading a gzip file
// we don't want to transparently gunzip it and save it as a .gz file.
(!path.endsWith(".gz")) && (!path.endsWith(".tgz")))

View File

@@ -300,9 +300,9 @@ public class FileUtil {
if (!_failedOracle) {
try {
Class<?> p200 = Class.forName("java.util.jar.Pack200", true, ClassLoader.getSystemClassLoader());
Method newUnpacker = p200.getMethod("newUnpacker", (Class[]) null);
Method newUnpacker = p200.getMethod("newUnpacker");
Object unpacker = newUnpacker.invoke(null,(Object[]) null);
Method unpack = unpacker.getClass().getMethod("unpack", new Class[] {InputStream.class, JarOutputStream.class});
Method unpack = unpacker.getClass().getMethod("unpack", InputStream.class, JarOutputStream.class);
// throws IOException
unpack.invoke(unpacker, new Object[] {in, out});
return;
@@ -321,9 +321,9 @@ public class FileUtil {
if (!_failedApache) {
try {
Class<?> p200 = Class.forName("org.apache.harmony.unpack200.Archive", true, ClassLoader.getSystemClassLoader());
Constructor<?> newUnpacker = p200.getConstructor(new Class[] {InputStream.class, JarOutputStream.class});
Object unpacker = newUnpacker.newInstance(new Object[] {in, out});
Method unpack = unpacker.getClass().getMethod("unpack", (Class[]) null);
Constructor<?> newUnpacker = p200.getConstructor(InputStream.class, JarOutputStream.class);
Object unpacker = newUnpacker.newInstance(in, out);
Method unpack = unpacker.getClass().getMethod("unpack");
// throws IOException or Pack200Exception
unpack.invoke(unpacker, (Object[]) null);
return;

View File

@@ -14,10 +14,13 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Like I2PThread but with per-thread OOM listeners,
* Like {@link I2PThread} but with per-thread OOM listeners,
* rather than a static router-wide listener list,
* so that an OOM in an app won't call the router listener
* to shutdown the whole router.
*
* This is preferred for application use.
* See {@link I2PThread} for features.
*/
public class I2PAppThread extends I2PThread {
@@ -38,9 +41,17 @@ public class I2PAppThread extends I2PThread {
public I2PAppThread(Runnable r, String name) {
super(r, name);
}
public I2PAppThread(Runnable r, String name, boolean isDaemon) {
super(r, name, isDaemon);
}
/**
* @since 0.9.23
*/
public I2PAppThread(ThreadGroup group, Runnable r, String name) {
super(group, r, name);
}
@Override
protected void fireOOM(OutOfMemoryError oom) {

View File

@@ -204,7 +204,15 @@ public class I2PSSLSocketFactory {
"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
// following is disabled because it is weak
// see e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=1107787
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"
// ??? "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"
//
// NOTE:
// If you add anything here, please also add to installer/resources/eepsite/jetty-ssl.xml
//
}));
/**

View File

@@ -14,76 +14,63 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* In case its useful later...
* (e.g. w/ native programatic thread dumping, etc)
*
* As of 0.9.21, I2PThreads are initialized to NORM_PRIORITY
* (not the priority of the creating thread).
* Preferred over {@link Thread} for all router uses.
* For applications, {@link I2PAppThread} is preferred.
* <p>
* Provides the following features:
* <ul>
* <li>Logging to wrapper log on unexpected termination in {@link #run()}.
* <li>Notification of OOM to registered listener (the router),
* which will cause logging to the wrapper log and a router restart
* <li>Catching and logging "OOM" caused by thread limit in {@link #start()}
* with distinct message, and does not call the OOM listener.
* <li>As of 0.9.21, initialization to NORM_PRIORITY
* (not the priority of the creating thread).
* </ul>
*/
public class I2PThread extends Thread {
/**
* Non-static to avoid refs to old context in Android.
* Probably should just remove all the logging though.
* Logging removed, too much trouble with extra contexts
*/
//private volatile Log _log;
private static final Set<OOMEventListener> _listeners = new CopyOnWriteArraySet<OOMEventListener>();
//private String _name;
//private Exception _createdBy;
public I2PThread() {
super();
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
public I2PThread(String name) {
super(name);
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
public I2PThread(Runnable r) {
super(r);
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
public I2PThread(Runnable r, String name) {
super(r, name);
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
public I2PThread(Runnable r, String name, boolean isDaemon) {
super(r, name);
setDaemon(isDaemon);
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
public I2PThread(ThreadGroup g, Runnable r) {
super(g, r);
setPriority(NORM_PRIORITY);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
}
/****
private void log(int level, String msg) { log(level, msg, null); }
private void log(int level, String msg, Throwable t) {
// we cant assume log is created
if (_log == null) _log = new Log(I2PThread.class);
if (_log.shouldLog(level))
_log.log(level, msg, t);
/**
* @since 0.9.23
*/
public I2PThread(ThreadGroup group, Runnable r, String name) {
super(group, r, name);
setPriority(NORM_PRIORITY);
}
****/
/**
* Overridden to provide useful info to users on OOM, and to prevent
* shutting down the whole JVM for what is most likely not a heap issue.
@@ -109,19 +96,9 @@ public class I2PThread extends Thread {
@Override
public void run() {
//_name = Thread.currentThread().getName();
//log(Log.INFO, "New thread started" + (isDaemon() ? " (daemon): " : ": ") + _name, _createdBy);
try {
super.run();
} catch (Throwable t) {
/****
try {
log(Log.CRIT, "Thread terminated unexpectedly: " + getName(), t);
} catch (Throwable woof) {
System.err.println("Died within the OOM itself");
t.printStackTrace();
}
****/
if (t instanceof OutOfMemoryError) {
fireOOM((OutOfMemoryError)t);
} else {
@@ -129,18 +106,8 @@ public class I2PThread extends Thread {
t.printStackTrace();
}
}
// This creates a new I2PAppContext after it was deleted
// in Router.finalShutdown() via RouterContext.killGlobalContext()
//log(Log.INFO, "Thread finished normally: " + _name);
}
/****
protected void finalize() throws Throwable {
//log(Log.DEBUG, "Thread finalized: " + _name);
super.finalize();
}
****/
protected void fireOOM(OutOfMemoryError oom) {
for (OOMEventListener listener : _listeners)
listener.outOfMemory(oom);

View File

@@ -763,7 +763,7 @@ public class LogManager implements Flushable {
private static final AtomicInteger __id = new AtomicInteger();
private class ShutdownHook extends Thread {
private class ShutdownHook extends I2PAppThread {
private final int _id;
public ShutdownHook() {
_id = __id.incrementAndGet();

View File

@@ -67,10 +67,12 @@ abstract class LogWriterBase implements Runnable {
public void run() {
_write = true;
// don't bother on Android
final boolean shouldReadConfig = !SystemVersion.isAndroid();
try {
while (_write) {
flushRecords();
if (_write)
if (_write && shouldReadConfig)
rereadConfig();
}
} catch (Exception e) {
@@ -145,7 +147,7 @@ abstract class LogWriterBase implements Runnable {
private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
String arrows = reverse ? "&darr;&darr;&darr;" : "^^^";
return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
_(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + '\n';
_t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + '\n';
}
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
@@ -154,7 +156,7 @@ abstract class LogWriterBase implements Runnable {
* gettext
* @since 0.9.3
*/
private String _(int a, String b, String c) {
private String _t(int a, String b, String c) {
return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
}

View File

@@ -33,6 +33,8 @@ public class PortMapper {
public static final String SVC_BOB = "BOB";
/** not necessary, already in config? */
public static final String SVC_I2CP = "I2CP";
/** @since 0.9.23 */
public static final String SVC_I2CP_SSL = "I2CP-SSL";
/**
* @param context unused for now
@@ -109,7 +111,7 @@ public class PortMapper {
* @since 0.9.20
*/
public void renderStatusHTML(Writer out) throws IOException {
List<String> services = new ArrayList(_dir.keySet());
List<String> services = new ArrayList<String>(_dir.keySet());
out.write("<h2>Port Mapper</h2><table><tr><th>Service<th>Host<th>Port\n");
Collections.sort(services);
for (String s : services) {

View File

@@ -51,7 +51,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private class CommandThread extends Thread {
private class CommandThread extends I2PAppThread {
private final boolean consumeOutput;
private final Object shellCommand;
private final Result result;
@@ -84,7 +84,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private static class StreamConsumer extends Thread {
private static class StreamConsumer extends I2PAppThread {
private final BufferedReader bufferedReader;
public StreamConsumer(InputStream inputStream) {
@@ -115,7 +115,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private static class StreamReader extends Thread {
private static class StreamReader extends I2PAppThread {
private final BufferedReader bufferedReader;
public StreamReader(InputStream inputStream) {
@@ -149,7 +149,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private static class StreamWriter extends Thread {
private static class StreamWriter extends I2PAppThread {
private final BufferedWriter bufferedWriter;
public StreamWriter(OutputStream outputStream) {

View File

@@ -6,6 +6,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.I2PAppContext;
@@ -38,7 +39,7 @@ public class SimpleTimer2 {
private static final int MAX_THREADS = 4;
private final ScheduledThreadPoolExecutor _executor;
private final String _name;
private volatile int _count;
private final AtomicInteger _count = new AtomicInteger();
private final int _threads;
/**
@@ -102,7 +103,7 @@ public class SimpleTimer2 {
super.afterExecute(r, t);
if (t != null) { // shoudn't happen, caught in RunnableEvent.run()
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer2.class);
log.log(Log.CRIT, "wtf, event borked: " + r, t);
log.log(Log.CRIT, "event borked: " + r, t);
}
}
}
@@ -110,18 +111,19 @@ public class SimpleTimer2 {
private class CustomThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread rv = Executors.defaultThreadFactory().newThread(r);
rv.setName(_name + ' ' + (++_count) + '/' + _threads);
rv.setName(_name + ' ' + _count.incrementAndGet() + '/' + _threads);
// Uncomment this to test threadgrouping, but we should be all safe now that the constructor preallocates!
// String name = rv.getThreadGroup().getName();
// if(!name.equals("main")) {
// (new Exception("OWCH! DAMN! Wrong ThreadGroup `" + name +"', `" + rv.getName() + "'")).printStackTrace();
// }
rv.setDaemon(true);
rv.setPriority(Thread.NORM_PRIORITY + 1);
return rv;
}
}
private ScheduledFuture schedule(TimedEvent t, long timeoutMs) {
private ScheduledFuture<?> schedule(TimedEvent t, long timeoutMs) {
return _executor.schedule(t, timeoutMs, TimeUnit.MILLISECONDS);
}
@@ -164,7 +166,8 @@ public class SimpleTimer2 {
* New code should use SimpleTimer2.TimedEvent.
*
* @since 0.9.20
* @param timeoutMs run first and subsequent iterations of this event every timeoutMs ms
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms, 5000 minimum
* @throws IllegalArgumentException if timeoutMs less than 5000
*/
public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long timeoutMs) {
addPeriodicEvent(event, timeoutMs, timeoutMs);
@@ -183,7 +186,8 @@ public class SimpleTimer2 {
*
* @since 0.9.20
* @param delay run the first iteration of this event after delay ms
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms, 5000 minimum
* @throws IllegalArgumentException if timeoutMs less than 5000
*/
public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long delay, final long timeoutMs) {
@@ -245,7 +249,7 @@ public class SimpleTimer2 {
private final SimpleTimer2 _pool;
private int _fuzz;
protected static final int DEFAULT_FUZZ = 3;
private ScheduledFuture _future; // _executor.remove() doesn't work so we have to use this
private ScheduledFuture<?> _future; // _executor.remove() doesn't work so we have to use this
// ... and I expect cancelling this way is more efficient
/** state of the current event. All access should be under lock. */
@@ -254,6 +258,8 @@ public class SimpleTimer2 {
private long _nextRun;
/** whether this was scheduled during RUNNING state. LOCKING: this */
private boolean _rescheduleAfterRun;
/** whether this was cancelled during RUNNING state. LOCKING: this */
private boolean _cancelAfterRun;
/** must call schedule() later */
public TimedEvent(SimpleTimer2 pool) {
@@ -286,12 +292,17 @@ public class SimpleTimer2 {
public synchronized void schedule(long timeoutMs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Scheduling: " + this + " timeout = " + timeoutMs + " state: " + _state);
if (timeoutMs <= 0 && _log.shouldLog(Log.WARN))
if (timeoutMs <= 0) {
// streaming timers do call with timeoutMs == 0
if (timeoutMs < 0 && _log.shouldLog(Log.WARN))
_log.warn("Timeout <= 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
timeoutMs = 1; // otherwise we may execute before _future is updated, which is fine
// except it triggers 'early execution' warning logging
}
// always set absolute time of execution
_nextRun = timeoutMs + System.currentTimeMillis();
_cancelAfterRun = false;
switch(_state) {
case RUNNING:
@@ -352,11 +363,13 @@ public class SimpleTimer2 {
* @param timeoutMs
*/
public synchronized void forceReschedule(long timeoutMs) {
cancel();
// don't cancel while running!
if (_state == TimedEventState.SCHEDULED)
cancel();
schedule(timeoutMs);
}
/** returns true if cancelled */
/** @return true if cancelled */
public synchronized boolean cancel() {
// always clear
_rescheduleAfterRun = false;
@@ -365,7 +378,9 @@ public class SimpleTimer2 {
case CANCELLED: // fall through
case IDLE:
break; // my preference is to throw IllegalState here, but let it be.
case RUNNING: // fall through
case RUNNING:
_cancelAfterRun = true;
return true;
case SCHEDULED:
boolean cancelled = _future.cancel(false);
if (cancelled)
@@ -378,27 +393,38 @@ public class SimpleTimer2 {
}
public void run() {
try {
run2();
} catch (RuntimeException re) {
_log.error("timer error", re);
throw re;
}
}
private void run2() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running: " + this);
long before = System.currentTimeMillis();
long delay = 0;
synchronized(this) {
if (_rescheduleAfterRun)
throw new IllegalStateException("rescheduleAfterRun cannot be true here");
throw new IllegalStateException(this + " rescheduleAfterRun cannot be true here");
switch(_state) {
case CANCELLED:
return; // goodbye
case IDLE: // fall through
case RUNNING:
throw new IllegalStateException("not possible to be in " + _state);
case SCHEDULED: // proceed, switch to IDLE in case I need to reschedule
_state = TimedEventState.IDLE;
throw new IllegalStateException(this + " not possible to be in " + _state);
case SCHEDULED:
// proceed, will switch to IDLE to reschedule
}
// if I was rescheduled by the user, re-submit myself to the executor.
int difference = (int)(_nextRun - before); // careful with long uptimes
long difference = _nextRun - before; // careful with long uptimes
if (difference > _fuzz) {
// proceed, switch to IDLE to reschedule
_state = TimedEventState.IDLE;
schedule(difference);
return;
}
@@ -411,12 +437,14 @@ public class SimpleTimer2 {
if (_future != null)
delay = _future.getDelay(TimeUnit.MILLISECONDS);
else if (_log.shouldLog(Log.WARN))
_log.warn(_pool + " wtf, no _future " + this);
_log.warn(_pool + " no _future " + this);
// This can be an incorrect warning especially after a schedule(0)
if (_log.shouldLog(Log.WARN) && delay > 100)
_log.warn(_pool + " wtf, early execution " + delay + ": " + this);
else if (_log.shouldLog(Log.WARN) && delay < -1000)
_log.warn(" wtf, late execution " + (0 - delay) + ": " + this + _pool.debug());
if (_log.shouldWarn()) {
if (delay > 100)
_log.warn(_pool + " early execution " + delay + ": " + this);
else if (delay < -1000)
_log.warn(" late execution " + (0 - delay) + ": " + this + _pool.debug());
}
try {
timeReached();
} catch (Throwable t) {
@@ -426,22 +454,27 @@ public class SimpleTimer2 {
switch(_state) {
case SCHEDULED: // fall through
case IDLE:
throw new IllegalStateException("can't be " + _state);
throw new IllegalStateException(this + " can't be " + _state);
case CANCELLED:
break; // nothing
case RUNNING:
_state = TimedEventState.IDLE;
// do we need to reschedule?
if (_rescheduleAfterRun) {
_rescheduleAfterRun = false;
schedule(_nextRun - System.currentTimeMillis());
if (_cancelAfterRun) {
_cancelAfterRun = false;
_state = TimedEventState.CANCELLED;
} else {
_state = TimedEventState.IDLE;
// do we need to reschedule?
if (_rescheduleAfterRun) {
_rescheduleAfterRun = false;
schedule(_nextRun - System.currentTimeMillis());
}
}
}
}
}
long time = System.currentTimeMillis() - before;
if (time > 500 && _log.shouldLog(Log.WARN))
_log.warn(_pool + " wtf, event execution took " + time + ": " + this);
_log.warn(_pool + " event execution took " + time + ": " + this);
if (_log.shouldLog(Log.INFO)) {
// this call is slow - iterates through a HashMap -
// would be better to have a local AtomicLong if we care
@@ -470,6 +503,7 @@ public class SimpleTimer2 {
return _executor.getCompletedTaskCount();
}
/** warning - slow */
private String debug() {
_executor.purge(); // Remove cancelled tasks from the queue so we get a good queue size stat
return
@@ -490,10 +524,13 @@ public class SimpleTimer2 {
* Schedule periodic event
*
* @param delay run the first iteration of this event after delay ms
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms, 5000 minimum
* @throws IllegalArgumentException if timeoutMs less than 5000
*/
public PeriodicTimedEvent(SimpleTimer2 pool, long delay, long timeoutMs) {
super(pool, delay);
if (timeoutMs < 5000)
throw new IllegalArgumentException("timeout minimum 5000");
_timeoutMs = timeoutMs;
}

View File

@@ -18,6 +18,8 @@ public abstract class SystemVersion {
private static final boolean _isArm = System.getProperty("os.arch").startsWith("arm");
private static final boolean _isX86 = System.getProperty("os.arch").contains("86") ||
System.getProperty("os.arch").equals("amd64");
private static final boolean _isGentoo = System.getProperty("os.version").contains("gentoo") ||
System.getProperty("os.version").contains("hardened"); // Funtoo
private static final boolean _isAndroid;
private static final boolean _isApache;
private static final boolean _isGNU;
@@ -27,6 +29,7 @@ public abstract class SystemVersion {
private static final boolean _oneDotSix;
private static final boolean _oneDotSeven;
private static final boolean _oneDotEight;
private static final boolean _oneDotNine;
private static final int _androidSDK;
static {
@@ -62,10 +65,12 @@ public abstract class SystemVersion {
_oneDotSix = _androidSDK >= 9;
_oneDotSeven = _androidSDK >= 19;
_oneDotEight = false;
_oneDotNine = false;
} else {
_oneDotSix = VersionComparator.comp(System.getProperty("java.version"), "1.6") >= 0;
_oneDotSeven = _oneDotSix && VersionComparator.comp(System.getProperty("java.version"), "1.7") >= 0;
_oneDotEight = _oneDotSeven && VersionComparator.comp(System.getProperty("java.version"), "1.8") >= 0;
_oneDotNine = _oneDotEight && VersionComparator.comp(System.getProperty("java.version"), "1.9") >= 0;
}
}
@@ -95,6 +100,13 @@ public abstract class SystemVersion {
return _isGNU;
}
/**
* @since 0.9.23
*/
public static boolean isGentoo() {
return _isGentoo;
}
/**
* @since 0.9.8
*/
@@ -139,6 +151,15 @@ public abstract class SystemVersion {
return _oneDotEight;
}
/**
*
* @return true if Java 1.9 or higher, false for Android.
* @since 0.9.23
*/
public static boolean isJava9() {
return _oneDotNine;
}
/**
* This isn't always correct.
* http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit

View File

@@ -65,7 +65,7 @@ public abstract class Translate {
* The {0} will be replaced by the parameter.
* Single quotes must be doubled, i.e. ' -> '' in the string.
* @param o parameter, not translated.
* To tranlslate parameter also, use _("foo {0} bar", _("baz"))
* To tranlslate parameter also, use _t("foo {0} bar", _t("baz"))
* Do not double the single quotes in the parameter.
* Use autoboxing to call with ints, longs, floats, etc.
*/

View File

@@ -351,7 +351,7 @@ public class TranslateReader extends FilterReader {
public void tag(List<String> args) {
if (args.size() <= 0)
return;
_out.print("\t_(");
_out.print("\t_t(");
for (int i = 0; i < args.size(); i++) {
if (i > 0)
_out.print(", ");
@@ -373,6 +373,9 @@ public class TranslateReader extends FilterReader {
}
}
/**
* Do not comment out, used to extract tags as a part of the build process.
*/
public static void main(String[] args) {
try {
if (args.length >= 2 && args[0].equals("test"))