/** * Prints a String to the log. * * @param buffer receiving byte buffer. * @param offset offset into the receiving buffer. * @param s the new string to be logged. * @return the new offset into the byte buffer. */ private int print(byte[] buffer, int offset, String s) { int length = s.length(); _cb.ensureCapacity(length); char[] cBuf = _cb.getBuffer(); s.getChars(0, length, cBuf, 0); for (int i = length - 1; i >= 0; i--) buffer[offset + i] = (byte) cBuf[i]; return offset + length; }
/** * Fills the time buffer with the formatted time. * * @param date current time in milliseconds */ private void fillTime(long date) throws IOException { synchronized (_timeBuffer) { if (date / 1000 == _lastTime / 1000) return; if (_timeFormatSecondOffset >= 0 && date / 3600000 == _lastTime / 3600000) { byte[] bBuf = _timeBuffer.getBuffer(); int min = (int) (date / 60000 % 60); int sec = (int) (date / 1000 % 60); bBuf[_timeFormatMinuteOffset + 0] = (byte) ('0' + min / 10); bBuf[_timeFormatMinuteOffset + 1] = (byte) ('0' + min % 10); bBuf[_timeFormatSecondOffset + 0] = (byte) ('0' + sec / 10); bBuf[_timeFormatSecondOffset + 1] = (byte) ('0' + sec % 10); _lastTime = date; return; } _timeCharBuffer.clear(); QDate.formatLocal(_timeCharBuffer, date, _timeFormat); if (_timeFormatSecondOffset >= 0) { _timeFormatSecondOffset = _timeCharBuffer.lastIndexOf(':') + 1; _timeFormatMinuteOffset = _timeFormatSecondOffset - 3; } char[] cBuf = _timeCharBuffer.getBuffer(); int length = _timeCharBuffer.getLength(); _timeBuffer.setLength(length); byte[] bBuf = _timeBuffer.getBuffer(); for (int i = length - 1; i >= 0; i--) bBuf[i] = (byte) cBuf[i]; } _lastTime = date; }
/** Parses the access log string. */ private ArrayList<Segment> parseFormat(String format) { ArrayList<Segment> segments = new ArrayList<Segment>(); CharBuffer cb = new CharBuffer(); int i = 0; while (i < _format.length()) { char ch = _format.charAt(i++); if (ch != '%' || i >= _format.length()) { cb.append((char) ch); continue; } String arg = null; ch = _format.charAt(i++); if (ch == '>') ch = _format.charAt(i++); else if (ch == '{') { if (cb.length() > 0) segments.add(new Segment(this, Segment.TEXT, cb.toString())); cb.clear(); while (i < _format.length() && _format.charAt(i++) != '}') cb.append(_format.charAt(i - 1)); arg = cb.toString(); cb.clear(); ch = _format.charAt(i++); } switch (ch) { case 'b': case 'c': case 'h': case 'i': case 'l': case 'n': case 'r': case 's': case 'T': case 'D': case 'o': case 'u': case 'U': case 'v': if (cb.length() > 0) segments.add(new Segment(this, Segment.TEXT, cb.toString())); cb.clear(); segments.add(new Segment(this, ch, arg)); break; case 't': if (cb.length() > 0) segments.add(new Segment(this, Segment.TEXT, cb.toString())); cb.clear(); if (arg != null) _timeFormat = arg; segments.add(new Segment(this, ch, arg)); break; default: cb.append('%'); i--; break; } } cb.append(CauchoSystem.getNewlineString()); segments.add(new Segment(this, Segment.TEXT, cb.toString())); return segments; }
/** * Logs a request using the current format. * * @param request the servlet request. * @param response the servlet response. * @param buffer byte buffer containing the response * @param offset buffer starting offset * @param length length allowed in the buffer * @return the new tail of the buffer */ private int log( HttpServletRequestImpl request, HttpServletResponseImpl responseFacade, AbstractHttpResponse response, byte[] buffer, int offset, int length) throws IOException { AbstractHttpRequest absRequest = request.getAbstractHttpRequest(); int len = _segments.length; for (int i = 0; i < len; i++) { Segment segment = _segments[i]; String value = null; CharSegment csValue = null; switch (segment._code) { case Segment.TEXT: int sublen = segment._data.length; byte[] data = segment._data; for (int j = 0; j < sublen; j++) buffer[offset++] = data[j]; break; case Segment.CHAR: buffer[offset++] = segment._ch; break; case 'b': if (responseFacade.getStatus() == 304) buffer[offset++] = (byte) '-'; else offset = print(buffer, offset, response.getContentLength()); break; // cookie case 'c': Cookie cookie = request.getCookie(segment._string); if (cookie == null) cookie = responseFacade.getCookie(segment._string); if (cookie == null) buffer[offset++] = (byte) '-'; else offset = print(buffer, offset, cookie.getValue()); break; // set cookie case Segment.SET_COOKIE: ArrayList<Cookie> cookies = responseFacade.getCookies(); if (cookies == null || cookies.size() == 0) buffer[offset++] = (byte) '-'; else { _cb.clear(); response.fillCookie(_cb, (Cookie) cookies.get(0), 0, 0, false); offset = print(buffer, offset, _cb.getBuffer(), 0, _cb.getLength()); } break; case 'h': if (isHostnameDnsLookup()) { String addrName = request.getRemoteAddr(); InetAddress addr = InetAddress.getByName(addrName); offset = print(buffer, offset, addr.getHostName()); } else offset = absRequest.printRemoteAddr(buffer, offset); break; // input header case 'i': csValue = absRequest.getHeaderBuffer(segment._string); if (csValue == null) buffer[offset++] = (byte) '-'; else offset = print(buffer, offset, csValue); break; case 'l': buffer[offset++] = (byte) '-'; break; // request attribute case 'n': Object oValue = request.getAttribute(segment._string); if (oValue == null) buffer[offset++] = (byte) '-'; else offset = print(buffer, offset, String.valueOf(oValue)); break; // output header case 'o': value = response.getHeader(segment._string); if (value == null) buffer[offset++] = (byte) '-'; else offset = print(buffer, offset, value); break; case 'r': offset = print(buffer, offset, request.getMethod()); buffer[offset++] = (byte) ' '; data = absRequest.getUriBuffer(); sublen = absRequest.getUriLength(); // server/02e9 if (buffer.length - offset - 128 < sublen) { sublen = buffer.length - offset - 128; System.arraycopy(data, 0, buffer, offset, sublen); offset += sublen; buffer[offset++] = (byte) '.'; buffer[offset++] = (byte) '.'; buffer[offset++] = (byte) '.'; } else { System.arraycopy(data, 0, buffer, offset, sublen); offset += sublen; } buffer[offset++] = (byte) ' '; offset = print(buffer, offset, request.getProtocol()); break; case 's': int status = responseFacade.getStatus(); buffer[offset++] = (byte) ('0' + (status / 100) % 10); buffer[offset++] = (byte) ('0' + (status / 10) % 10); buffer[offset++] = (byte) ('0' + status % 10); break; case 't': long date = Alarm.getCurrentTime(); if (date / 1000 != _lastTime / 1000) fillTime(date); sublen = _timeBuffer.getLength(); data = _timeBuffer.getBuffer(); synchronized (_timeBuffer) { System.arraycopy(data, 0, buffer, offset, sublen); } offset += sublen; break; case 'T': { long startTime = request.getStartTime(); long endTime = Alarm.getCurrentTime(); offset = print(buffer, offset, (int) ((endTime - startTime + 500) / 1000)); break; } case 'D': { long startTime = request.getStartTime(); long endTime = Alarm.getCurrentTime(); offset = print(buffer, offset, (int) ((endTime - startTime) * 1000)); break; } case 'u': value = request.getRemoteUser(false); if (value == null) buffer[offset++] = (byte) '-'; else { buffer[offset++] = (byte) '"'; offset = print(buffer, offset, value); buffer[offset++] = (byte) '"'; } break; case 'v': value = request.getServerName(); if (value == null) buffer[offset++] = (byte) '-'; else { offset = print(buffer, offset, value); } break; case 'U': offset = print(buffer, offset, request.getRequestURI()); break; default: throw new IOException(); } } return offset; }
/** * Serializes a <code>DBObject</code> into a string that can be sent to the database. * * <p>There is a pool of available encoders. Create a new one as follows: * * <blockquote> * * </pre> * * ByteEncoder encoder = ByteEncoder.get(); // try forever until an encoder is available </pre> * * </blockquote> * * <p>Each key/value pair in the <code>DBObject</code> is encoded in the following format: * * <blockquote> * * <i><type (</i><code>byte</code><i>)><name (</i><code>String</code></i>)><0 (</i> * <code>byte</code><i>)><data (serialized data)></i> * * </blockquote> * * For example: * * <blockquote> * * <<code>NUMBER</code>><name>0<double> <code>// NUMBER = 1</code><br> * <<code>STRING</code>><name>0<len><string>0 <code>// STRING = 2</code> * * </blockquote> */ public class ByteEncoder extends Bytes { static final boolean DEBUG = Boolean.getBoolean("DEBUG.BE"); // things that won't get sent in the scope static final Set<String> BAD_GLOBALS = new HashSet<String>(); static { BAD_GLOBALS.add("db"); BAD_GLOBALS.add("local"); BAD_GLOBALS.add("core"); BAD_GLOBALS.add("args"); // TODO: should we get rid of this BAD_GLOBALS.add("obj"); // TODO: get rid of this } /** * Whether a given field is generated by the db for use by the db * * @param o an object to check * @return whether the object is a db field */ public static boolean dbOnlyField(Object o) { if (o == null) return false; if (o instanceof String) return dbOnlyField(o.toString()); return false; } /** * Whether a given field is generated by the db for use by the db * * @param s a string to check * @return whether the string is a db field */ public static boolean dbOnlyField(String s) { return s.equals("_ns") || s.equals("_save") || s.equals("_update"); } /** * Fetches a new <code>ByteEncoder</code> from the pool of available <code>ByteEncoder</code>s. * * @return a new <code>ByteEncoder</code> */ public static ByteEncoder get() { return _pool.get(); } /** * Get the current position of this ByteEncoder * * @return */ public long getPosition() { return _buf.position(); } /** * Get the remaining capacity of this ByteEncoder * * @return */ public long getRemaining() { return _buf.remaining(); } /** Resets and returns this encoder to the pool. */ protected void done() { reset(); _pool.done(this); } static final SimplePool<ByteEncoder> _pool = new SimplePool<ByteEncoder>("ByteEncoders", NUM_ENCODERS, NUM_ENCODERS * 2) { protected ByteEncoder createNew() { if (D) System.out.println("creating new ByteEncoder"); return new ByteEncoder(); } protected long memSize(ByteEncoder d) { return d._buf.capacity() + (2 * MAX_STRING) + 1024; } }; // ---- private ByteEncoder() { _buf = ByteBuffer.allocateDirect(MAX_OBJECT_SIZE + 2048); _buf.order(Bytes.ORDER); } /** * Returns the bytes in the bytebuffer. Attempts to leave the bytebuffer in the same state. Note * that mark, if set, is lost. * * @return array of bytes */ public byte[] getBytes() { int pos = _buf.position(); int limit = _buf.limit(); flip(); byte[] arr = new byte[_buf.limit()]; _buf.get(arr); flip(); _buf.position(pos); _buf.limit(limit); return arr; } /** Returns encoder to its starting state, ready to encode an object. */ protected void reset() { _buf.position(0); _buf.limit(_buf.capacity()); _flipped = false; _dontRef.clear(); } /** Switches the encoder from being write-only to being read-only. */ protected void flip() { _buf.flip(); _flipped = true; } /** * Encodes a <code>DBObject</code>. This is for the higher level api calls * * <p>If encoding an object fails, the buffer will be reset to the position prior to this put call * and a BufferOverflowException will be thrown * * @param o the object to encode * @return the number of characters in the encoding */ public int putObject(DBObject o) { _buf.mark(); try { return putObject(null, o); } catch (BufferOverflowException bof) { // reset to marked offset and wipe any written data _buf.reset(); _buf.put(new byte[_buf.remaining()]); _buf.reset(); throw new BufferOverflowException(); } } /** this is really for embedded objects */ private int putObject(String name, DBObject o) { if (o == null) throw new NullPointerException("can't save a null object"); if (DEBUG) System.out.println( "putObject : " + name + " [" + o.getClass() + "]" + " # keys " + o.keySet().size()); if (_flipped) throw new IllegalStateException("already flipped"); final int start = _buf.position(); byte myType = OBJECT; if (o instanceof List) myType = ARRAY; if (_handleSpecialObjects(name, o)) return _buf.position() - start; if (name != null) { _put(myType, name); } final int sizePos = _buf.position(); _buf.putInt(0); // leaving space for this. set it at the end List transientFields = null; if (myType == OBJECT) { if (o.containsField("_id")) _putObjectField("_id", o.get("_id")); { Object temp = o.get("_transientFields"); if (temp instanceof List) transientFields = (List) temp; } } for (String s : o.keySet()) { if (s.equals("_id")) continue; if (transientFields != null && transientFields.contains(s)) continue; Object val = o.get(s); _putObjectField(s, val); } _buf.put(EOO); _buf.putInt(sizePos, _buf.position() - sizePos); return _buf.position() - start; } private void _putObjectField(String name, Object val) { if (dbOnlyField(name) || name.equals("_transientFields")) return; if (DEBUG) System.out.println("\t put thing : " + name); if (name.equals("$where") && val instanceof String) { _put(CODE, name); _putValueString(val.toString()); return; } val = Bytes.applyEncodingHooks(val); if (val == null) putNull(name); else if (val instanceof Date) putDate(name, (Date) val); else if (val instanceof Number) putNumber(name, (Number) val); else if (val instanceof String) putString(name, val.toString()); else if (val instanceof ObjectId) putObjectId(name, (ObjectId) val); else if (val instanceof DBObject) putObject(name, (DBObject) val); else if (val instanceof Boolean) putBoolean(name, (Boolean) val); else if (val instanceof Pattern) putPattern(name, (Pattern) val); else if (val instanceof DBRegex) { putDBRegex(name, (DBRegex) val); } else if (val instanceof Map) putMap(name, (Map) val); else if (val instanceof List) putList(name, (List) val); else if (val instanceof byte[]) putBinary(name, (byte[]) val); else if (val instanceof DBBinary) putBinary(name, (DBBinary) val); else if (val.getClass().isArray()) putList(name, Arrays.asList((Object[]) val)); else if (val instanceof DBPointer) { // temporary - there's the notion of "special object" , but for simple level 0... DBPointer r = (DBPointer) val; putDBPointer(name, r._ns, (ObjectId) r._id); } else if (val instanceof DBRefBase) { putDBRef(name, (DBRefBase) val); } else if (val instanceof DBSymbol) { putSymbol(name, (DBSymbol) val); } else if (val instanceof DBUndefined) { putUndefined(name); } else if (val instanceof DBTimestamp) { putTimestamp(name, (DBTimestamp) val); } else throw new IllegalArgumentException("can't serialize " + val.getClass()); } private void putList(String name, List l) { _put(ARRAY, name); final int sizePos = _buf.position(); _buf.putInt(0); for (int i = 0; i < l.size(); i++) _putObjectField(String.valueOf(i), l.get(i)); _buf.put(EOO); _buf.putInt(sizePos, _buf.position() - sizePos); } private void putMap(String name, Map m) { _put(OBJECT, name); final int sizePos = _buf.position(); _buf.putInt(0); for (Object key : m.keySet()) _putObjectField(key.toString(), m.get(key)); _buf.put(EOO); _buf.putInt(sizePos, _buf.position() - sizePos); } private boolean _handleSpecialObjects(String name, DBObject o) { if (o == null) return false; if (o instanceof DBCollection) { DBCollection c = (DBCollection) o; putDBPointer(name, c.getName(), Bytes.COLLECTION_REF_ID); return true; } if (!_dontRefContains(o) && name != null && o instanceof DBPointer) { DBPointer r = (DBPointer) o; putDBPointer(name, r._ns, (ObjectId) r._id); return true; } if (!(o instanceof List) && o.get(Bytes.NO_REF_HACK) != null) { o.removeField(Bytes.NO_REF_HACK); return false; } if (!_dontRefContains(o) && name != null && !(o instanceof List) && cameFromDB(o)) { putDBPointer(name, o.get("_ns").toString(), (ObjectId) (o.get("_id"))); return true; } return false; } protected int putNull(String name) { int start = _buf.position(); _put(NULL, name); return _buf.position() - start; } protected int putUndefined(String name) { int start = _buf.position(); _put(UNDEFINED, name); return _buf.position() - start; } protected int putTimestamp(String name, DBTimestamp ts) { int start = _buf.position(); _put(TIMESTAMP, name); _buf.putInt(ts.getTime()); _buf.putInt(ts.getInc()); return _buf.position() - start; } protected int putBoolean(String name, Boolean b) { int start = _buf.position(); _put(BOOLEAN, name); _buf.put(b ? (byte) 0x1 : (byte) 0x0); return _buf.position() - start; } protected int putDate(String name, Date d) { int start = _buf.position(); _put(DATE, name); _buf.putLong(d.getTime()); return _buf.position() - start; } protected int putNumber(String name, Number n) { int start = _buf.position(); if (n instanceof Integer) { _put(NUMBER_INT, name); _buf.putInt(n.intValue()); } else if (n instanceof Long) { _put(NUMBER_LONG, name); _buf.putLong(n.longValue()); } else { _put(NUMBER, name); _buf.putDouble(n.doubleValue()); } return _buf.position() - start; } protected void putBinary(String name, byte[] data) { _put(BINARY, name); _buf.putInt(4 + data.length); _buf.put(B_BINARY); _buf.putInt(data.length); int before = _buf.position(); _buf.put(data); int after = _buf.position(); com.mongodb.util.MyAsserts.assertEquals(after - before, data.length); } protected void putBinary(String name, DBBinary val) { _put(BINARY, name); _buf.putInt(val._data.length); _buf.put(val._type); _buf.put(val._data); } protected int putSymbol(String name, DBSymbol s) { return _putString(name, s.getSymbol(), SYMBOL); } protected int putString(String name, String s) { return _putString(name, s, STRING); } private int _putString(String name, String s, byte type) { int start = _buf.position(); _put(type, name); _putValueString(s); return _buf.position() - start; } protected int putObjectId(String name, ObjectId oid) { int start = _buf.position(); _put(OID, name); _buf.putInt(oid._time); _buf.putInt(oid._machine); _buf.putInt(oid._inc); return _buf.position() - start; } protected int putDBPointer(String name, String ns, ObjectId oid) { int start = _buf.position(); _put(REF, name); _putValueString(ns); _buf.putInt(oid._time); _buf.putInt(oid._machine); _buf.putInt(oid._inc); return _buf.position() - start; } protected void putDBRef(String name, DBRefBase ref) { _put(OBJECT, name); final int sizePos = _buf.position(); _buf.putInt(0); _putObjectField("$ref", ref.getRef()); _putObjectField("$id", ref.getId()); _buf.put(EOO); _buf.putInt(sizePos, _buf.position() - sizePos); } private int putDBRegex(String name, DBRegex regex) { int start = _buf.position(); _put(REGEX, name); _put(regex.getPattern()); String options = regex.getOptions(); TreeMap<Character, Character> sm = new TreeMap<Character, Character>(); for (int i = 0; i < options.length(); i++) { sm.put(options.charAt(i), options.charAt(i)); } StringBuffer sb = new StringBuffer(); for (char c : sm.keySet()) { sb.append(c); } _put(sb.toString()); return _buf.position() - start; } private int putPattern(String name, Pattern p) { int start = _buf.position(); _put(REGEX, name); _put(p.pattern()); _put(patternFlags(p.flags())); return _buf.position() - start; } // ---------------------------------------------- /** Encodes the type and key. */ private void _put(byte type, String name) { _buf.put(type); _put(name); } void _putValueString(String s) { int lenPos = _buf.position(); _buf.putInt(0); // making space for size int strLen = _put(s); _buf.putInt(lenPos, strLen); } int _put(String name) { _cbuf.position(0); _cbuf.limit(_cbuf.capacity()); _cbuf.append(name); _cbuf.flip(); final int start = _buf.position(); _encoder.encode(_cbuf, _buf, false); _buf.put((byte) 0); return _buf.position() - start; } boolean _dontRefContains(Object o) { if (_dontRef.size() == 0) return false; return _dontRef.peek().contains(o); } private final CharBuffer _cbuf = CharBuffer.allocate(MAX_STRING); private final CharsetEncoder _encoder = _utf8.newEncoder(); private Stack<IdentitySet> _dontRef = new Stack<IdentitySet>(); private boolean _flipped = false; final ByteBuffer _buf; }