Hollow-Expected class sun.util.calendar.BaseCalendar$Date but object was class java.util.Date

ObjectMapper写入一个数据,包含一个java.util.Date成员,报Expected class sun.util.calendar.BaseCalendar$Date but object was class java.util.Date错误。

这是hollow v2.1.0以下的一个bug,详见:

https://github.com/Netflix/hollow/issues/13

Thanks very much for the report! I verified the issue and released a fix in v2.1.1.

有趣的是,将hollow替换成2.1.1,还是报这个错误,替换成2.2.1就好了

这句I verified the issue and released a fix in v2.1.1.像极了研发的口头禅‘下个版本修复’,承包了我一上午的笑点。

Jdom-XmlOutputter 换行(setIndent)

Jdom的XmlOutputter默认生成的文件不带换行,所有key-value对写在一行里,使用起来很不方便,XmlOutputter支持设置换行。

jdom1.0中写法如下

XmlOutputter xmlOut = new XmlOutputter("  ", true, "utf-8");

三个参数分别为,缩进(这里是两个空格),是否换行,字符编码

官方文档

jdom1.0以后不支持上面的写法,而是把三个参数剥离出来,形成了Format类

Format format = Format.getCompactFormat();
format.setEncoding("utf-8");
format.setIndent("  ");
XMLOutputter XMLOut = new XMLOutputter(format);

d3总结

项目原因,需要使用web绘制图表,比较靠谱的有echart和d3。

echart:应该是百度的项目,首页做的很漂亮,文档也比较全。个人感觉是一个入手较易,主要在学习怎么配参数,地图是echart的一个亮点。缺点就明显了,入手容易的肯定高度封装过,很难根据自己的需求定制化。而且使用了cavas绘图,在移动端性能不如svg。个人感觉echart适合一些时间比较紧迫,设计没那么明确的场合,直接套上echart,就已经很漂亮了。

d3:大名鼎鼎,受欢迎程度超过jquery,首页的绚丽特效让人眼花,教程的话强烈推荐http://www.ourd3js.com/wordpress/,这个教程好像出了书,叫精通D3.js,基本上把教程撸一遍就没啥大问题了。

d3的优点:用过一段d3后,感觉d3就是svg的jquery,把svg的操作封装为更易用的接口,并提供各种数据可视化的接口。所以d3很灵活,因为你可以用d3在svg中添加一个点、线、饼等等,svg性能不错。链式写法。

缺点:入手稍微慢点,不过撸一遍教程也就一下午的时间。svg的接口网上文档比较少,有些用法stackoverflow都找不到。

如果是学习d3的,推荐前面的教程网站,这里只是个人备忘录,想到哪里说到哪里的流水账,记录那些让人眼前一亮或者眼前一黑的点,以及几个开发中反复查询的用法。

好了,出发

1.

var svg = d3.select("body")
    .append("svg")
    .attr("width", svgWidth)
    .attr("height", svgHeight);

首先,你要在一个dom中添加svg,需要两个参数,width和height,一般我是这么给的

this.svgContent = document.getElementById("id");
this.svgWidth = this.svgContent.clientWidth;
this.svgHeight = this.svgContent.clientHeight;

踩坑:需求在指定操作后才绘制该图标,dom display:none的时候,clientWidth和clientHeight是0,所以svg等dom display正常后再添加。

接下来最好设置一下svg的padding,用来应付后面出现的各种文字绘制超出不显示,UI修改等灵细操作。

this.padding = {left: Ruler.size_1920(200), right: Ruler.size_1920(220), top: Ruler.size_1920(66), bottom: Ruler.size_1920(28)};

因为svg直接传的都是px,所以需要根绝屏幕做一下自适应,size_1920函数如下:

let deviceWidth = document.documentElement.clientWidth;
return (px * deviceWidth) / 1920;

坐标轴:

图标中用到最多的是坐标轴,使用坐标轴首先要设定比例尺,目前共用到了4种比例尺

线性比例尺

this.yScale = d3.scale.linear()
   .domain([0, this.threshold])
   .range([this.svgHeight - this.padding.top - this.padding.bottom, 0]);

domain中设置数据的min/max,range中设置svg实际位置。

备注:svg是以左上角为原点的,而常规视角中原点是左下角,所以这里的range起止位置是 svgHeight-0,即起始位置是下边缘y=svgHeight,终止位置是上边缘y=0。这一点需要谨记,后面有许多计算x、y位置的,y的位置总要反着想。其他比例尺也是一样的。

离散比例尺

this.xScale = d3.scale.ordinal()
    .domain(d3.range(data.length))
    .rangeRoundBands([0, this.svgWidth - this.padding.left - this.padding.right]);

or

this.xScale = d3.scale.ordinal()
    .domain(["1","2","3","4"])
    .rangeRoundBands([0, this.svgWidth - this.padding.left - this.padding.right]);

时间比例尺

时间比例尺适用于一些以时间为坐标轴的情况

this.now = new Date();
let nextDay = new Date(this.now.getTime() + 1000 * 3600 * 24);
// 这里要搞成UTC时间
this.nowUTC = this.now.getUTCFullYear() + "-" + (this.now.getUTCMonth() + 1) + "-" + (this.now.getUTCDate() < 10 ? "0" : "") + this.now.getUTCDate() + "T00:00";
this.nextUTC = nextDay.getUTCFullYear() + "-" + (nextDay.getUTCMonth() + 1) + "-" +
    (nextDay.getUTCDate() < 10 ? "0" : "") + nextDay.getUTCDate() + "T00:00";
this.xScale = d3.time.scale()
    .domain([new Date(this.nowUTC), new Date(this.nextUTC)])
    .range([0, this.svgWidth - this.padding.left - this.padding.right]);

备注:比如这里是今天到明天,第二天nextDay的算法,就用秒,其他都不行。UTC时间一定要注意,所有时间都改成UTC时间,所有get一律要加UTC。

颜色比例尺

// 颜色比例尺
var color = d3.scale.category10() // 20  30 都有

这是个比较特殊的比例尺,不是用来画坐标轴的,而是把颜色均匀分割,category10,category20等等都有,填一个index,输出一个色值。在有些d3的版本中,颜色比例尺没用放在scale里面。

坐标轴

可能还有更多种类的比例尺,目前还没有接触,下面就开始生成坐标轴数据。

let gridXAxis = d3.svg.axis()
    .scale(this.xScale)
    .orient("top")
    .innerTickSize(this.svgHeight - this.padding.top - this.padding.bottom)
    .outerTickSize(0)
    .ticks(d3.time.hour, 1)
    .tickFormat(function (d, i) {
        return "";
    })
    .tickPadding(10);

几个常用参数

scale,必要参数,把比例尺填进去

orient,非必填,刻度相对坐标轴的位置 top bottom left right

ticks,非必填,刻度个数 注:ticks只是参考,最终生成刻度个数以数据为准,比如数据需要25个刻度,但ticks填了17,那d3就没办法了,会生成一个d3生成的刻度个数中选跟17最接近的。

innerTickSize,非必填,内刻度高,默认是6。 注:这里设置了innerTickSize是整个svg的高度,和y轴innerTickSize设置整个svg的宽配合行程网格效果,正常的坐标轴就再画一个,注意高度计算时错开

let xAxis = d3.svg.axis()
    .scale(this.xScale)
    .orient("bottom")
    .ticks(d3.time.hour, 1)
    .outerTickSize(0)
    .tickFormat(function (d, i) {
    });

outerTickSize,非必填,外刻度高

tickFormat,非必填,刻度值,默认为空  注:这是一个数据绑定参数,即根据数据生成不同的值

tickPadding,非必填,刻度文字与坐标轴的间距

真正的绘制

this.svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(" + this.padding.left + "," + (this.svgHeight - this.padding.top + this.xPadding + this.doubleLinePadding) + ")")
    .call(xAxis);
this.svg.append("g")
    .attr("class", "x axis grid")
    .attr("transform", "translate(" + this.padding.left + "," + (this.svgHeight - this.padding.top + this.xPadding) + ")")
    .call(gridXAxis);

 

给svg中添加g元素,call绑定坐标轴数据,attr就是给g元素添加各种属性,class(是不是很熟悉),transform。 注:svg中没有top left  margin等属性,大的位移使用transform。

曲线

坐标轴绘制完后,就是曲线的绘制,先生成曲线绘制函数

let _line = d3.svg.line()
    .x(function (d) {
        let x = self.xScale(d.time);
        if (x < 0) {
            return 0;
        }
        return self.xScale(d.time);
    })
    .y(function (d) {
        let x = self.xScale(d.time);
        if (x < 0) {
            return 0;
        }
        return self.yScale(d.uv);
    })
    // .interpolate("cardinal");
    .interpolate("monotone");

曲线绘制函数参数

x、y就是根据数据来计算坐标,这里是比例尺大显身手的地方。

interpolate,线段怎么弯曲,讲的最好的是这个地址

http://www.oxxostudio.tw/articles/201411/svg-d3-02-line.html

注:basis曲线保证了优美,没保证曲线与数据的一致,像我这种人是完全无法接受的。

绘制曲线

let paths = this.svg.selectAll(".data-line").data(data);

这一行代码解读一下就是选择所有class是data-line的元素,然后给它绑定数据。data-line哪里来的?这里涉及到d3中一个概念,update  enter  exit

selectAll得到的元素跟数据相比,有三种可能,元素个数 大于/小于/等于数据个数。

数据超出元素个数的部分叫enter,比如第一次绘制时,所有元素都属于paths.enter

paths.enter()
    .append("path")
    .attr("class", "data-line gaussian-shadow")
    .attr("d", function (d) {
            return _line(d.data);
    })
    .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")")
    .style("stroke", function (d, i) {
        return d3.scale.category10(i);
    })
    .attr("fill", "none");

paths元素比较简单直接,基本上只需要给一个属性d就能画出来了,d可以用前面的曲线函数直接生成。

stroke,线条颜色,这里可以用颜色比例尺 d3.scale.category10(i)

fill就是填充颜色,paths填充的是一个面。

不超出的部分叫update,即需要更新的部分,paths.update可以看到,也可以直接更新数据,这里是数据驱动的最直观表现

paths.attr("d", function (d) {
    if (d && d.data) {
        return _line(d.data);
    }
});

数据少于元素个数的部分叫exit,一般exit只有一个用法

// 去掉多余的曲线
paths.exit().remove();

到这里,一个坐标轴差不多出来了,但,是不是少了点啥,对,UED最喜欢的灵魂一击,动画。

曲线进入/更新的时候要动画怎么办

d3的动画很好写

paths.transition()
    .duration(2000)
    .ease("sin")
    .attr("d", function (d) {
        if (d && d.data) {
            return _line(d.data);
        }
    });

增加 transition ease 和 delay即可,transition前后是动画的起始和结束状态。

绘制一个圆

let circles = this.svg.selectAll("circle")
    .data(this.axisData);
circles.attr("cx", this.xScale(this.now))
        .attr("cy", function (d) {
            if (d && d.data) {
                return this.yScale(d.data)
            }
        })
}
circles.exit().remove();
circles.enter()
    .append("circle")
    .attr("fill", function (d) {
        return d.color;
    })
    .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")")
    // .attr("fill-opacity", "0.4")
    .attr("cx", this.xScale(this.now))
    .attr("cy", function (d) {
        if (d && d.data) {
            return this.yScale(d.data)
        }
    })
    .attr("r", 10);

矩形

let rectContainers = this.svg.selectAll("item-title")
    .data(data)
    .enter()
    .append("g")
    .attr("class", "item-title")
    .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")");
rectContainers.append("rect")
    .attr("fill", function (d) {
        return d.color;
    })
    .attr("x", this.xScale(this.now) + 30)
    .attr("y", function (d) {
        if (d && d.data) {
            return yScale(d.data);
        }
    })
    .attr("width", function (d) {
        return 20;
    })
    .attr("height", rectHeight)
    .attr("rx", rectRadius)
    .attr("ry", rectRadius)
    .attr("fill-opacity", 0.8);

半圆角矩形是用path生成的,顶部圆角矩形

var topRoundedRect = function(x, y, width, height, radius) {
    return "M" + x + "," + y
        + "v" + ( radius - height)
        + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + -radius
        + "h" + (width - 2 * radius)
        + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius
        + "v" + (height - radius)
        + "z";
};
var rightRoundedRect = function(x, y, width, height, radius) {
    return "M" + x + "," + y
        + "h" + (width - radius)
        + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius
        + "v" + (height - 2 * radius)
        + "a" + radius + "," + radius + " 0 0 1 " + -radius + "," + radius
        + "h" + (radius - width)
        + "z";
};
this.svg.selectAll(".data-rect")
 .data(data)
 .enter()
 .append("path")
 .attr("class", "data-rect")
 .attr("transform", "translate(" + this.padding.left + "," + this.padding.top + ")")
 .attr("fill", function(d, i) {
 return this.colors[i];
 }.bind(this))
 .attr("d", function (d, i) {
 let x = this.xScale(i) + this.rectPadding/2;
 let y = this.yScale(this.yScale.domain()[0]);
 let radius = 0;
 let width = this.xScale.rangeBand() - this.rectPadding;
 if (d.albm_cnt != 0) {
 radius = this.rectRadius;
 }
 let height = this.yScale(this.yScale.domain()[0]) - (this.yScale(d.albm_cnt)) + this.extraHeight;
 return topRoundedRect(x, y, width, height, radius);
 }.bind(this));

topRoundRect和rightRoundRect乍一看完全搞不懂,这里讲的比较详细https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths,仔细观察下,就是

M  起始x y坐标

h 纵移

v 横移

a 比较复杂 前两个参数是圆角的x y radius

中间三个参数 001是三个flag,只能是0/1 圆角矩形都是001

第一个代表弧线角度是否要大于180

第二个代表正角度还是负角度

第三个代表正方向还是反方向

z是合拢

最后两个参数是终止的x y位置

当前了,还有更多的L C S等等,这里不研究了

文字

rectContainers.append("text")
    .attr("class", "item-text")
    .attr("fill", "white")
    .attr("x", this.xScale(this.now) + 30 + 14)
    .attr("y", function (d) {
        if (d && d.dataSet) {
            return  self.yScale(AxisView.getPvByTime(d.dataSet, self.now)) + 6;
        }
    })
    .text(function (d) {
        return d.type;
    });

到这里,d3绘制的套路差不多都熟悉了,append data enter transform attr,拿来画个饼图吧!

生成饼图数据

let pie = d3.layout.pie().sort(null);
this.pieData = pie(percentArray);

查看一下this.pieData,你会发现数据增加了 startEngle 和 endEngle

注:饼图默认会按大小排序,如果不需要排序,增加一个sort(null)即可

弧线函数

this.arc = d3.svg.arc()
    .innerRadius(0)
    .outerRadius(self.innerRadius);
let arcs = this.svg.selectAll(".time-pie")
    .data(this.pieData)
    .enter()
    .append("g")
    .attr("class", "time-pie")
    .attr("transform", "translate(" + this.centerX + "," + this.centerY + ")");
arcs.append("path")
    .attr("fill", function (d, i) {
        return this.colors[i];
    }.bind(this))
    .attr('d', function(d) {
        return this.arc(d);
    }.bind(this));

饼图动画不能使用d了,要使用attrTween

简单的饼图动画

arcs.append("path")
    .attr("fill", "#fff")
    .attr("fill-opacity", 0.2)
    .transition()
    .duration(this.fadeInTime)
    .attrTween('d', function(d) {
        var i = d3.interpolate(d.startAngle+0.1, d.endAngle);
        return function(t) {
            d.endAngle = i(t);
            return arc(d);
        };
    }.bind(this));

高级饼图动画,自己计算delay的时间,让饼一点一点出来

arcs.append("path")
    .attr("fill", function (d, i) {
        return this.colors[i];
    }.bind(this))
    .transition()
    .delay(function (d, i) {
        if (data) {
            let _percent = 0;
            for (let j = 0; j < i; j++) {
                try {
                    _percent += parseInt(data[j].percent)
                } catch (e) {
                    _percent += 100 / data.length;
                }
            }
            return this.pieAnimTime * _percent / 100;
        }
    }.bind(this))
    .duration(function (d) {
        let _time = this.pieAnimTime / data.length;
        if (d && d.data) {
            let percent = parseInt(d.data);
            _time = this.pieAnimTime * percent / 100;
        }
        this.lastAnimTime+=_time;
        return _time;
    }.bind(this))
    .ease("linear")
    .attrTween('d', function(d) {
        var i = d3.interpolate(d.startAngle+0.1, d.endAngle);
        return function(t) {
            d.endAngle = i(t);
            return this.arc(d);
        }.bind(this);
    }.bind(this));

往饼图中间添加一个圆点

this.centriodData = Array.apply([0,0], this.pieData).map(function (val, index) {
    // return this.arc.centroid(val);
    return {
        right: (val.startAngle+val.endAngle)/2<Math.PI,
        centroidPos: this.arc.centroid(val)
    };
    return this.arc.centroid(val);
}.bind(this));

注:centriodData和Angle是钝/锐角的计算提出来,是因为在某些低配机子上,偶现绑定计算错误。

this.svg.selectAll(".time-circle")
    .data(this.pieData)
    .enter().append("circle")
    .attr("class", "time-circle")
    .attr("transform", "translate(" + this.centerX + "," + this.centerY + ")")
    .attr("cx", function(d, i) {
        // var pos= this.arc.centroid(d);
        var pos= this.centriodData[i] && this.centriodData[i].centroidPos;
        return pos && pos[0] * 1.65;
    }.bind(this))
    .attr("cy", function(d, i) {
        // var pos= this.arc.centroid(d);
        var pos= this.centriodData[i] && this.centriodData[i].centroidPos;
        return pos && pos[1] * 1.65;
    }.bind(this))
    .attr("fill", "#fff")
    // .attr("fill-opacity", 0.1)
    // .transition()
    // .duration(this.fadeInTime)
    .attr("fill-opacity", 1)
    .attr("r", Ruler.size_1920(6));

画一个折线

let line = this.svg.selectAll(".line")      //添加文字和弧之间的连线
    .data(this.pieData) //返回是pie(data0)
    .enter().append("g")
    .attr("class", "line")
    .attr("transform", "translate(" +  this.centerX + "," + this.centerY + ")")
    .append("polyline")
    .attr('points', function(d, i) {
        // var pos1= this.arc.centroid(d),pos2= this.arc.centroid(d),pos3= this.arc.centroid(d);
        var pos1=[this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]] ,pos2= [this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]],pos3= [this.centriodData[i].centroidPos[0], this.centriodData[i].centroidPos[1]];
        pos1[0]*=1.65,pos1[1]*=1.65;
        pos2[0]*=2,pos2[1]= pos2[1] *2;
        pos3[0]=(this.centriodData[i].right?this.lineLen:-this.lineLen);
        pos3[1]= pos3[1]*2;
        //pos1表示圆弧的中心往上,pos2是圆弧边,pos3就是将pos2平移后得到的位置
        //三点链接在一起就成了线段。
        return [pos1,pos2,pos3];
    }.bind(this))
    .style('fill', 'none')
    .style('stroke', "#fff")
    .style('stroke-opacity', 0.6)
    .style('stroke-width', Ruler.size_1920(3) + "px");
    // 这里是动画,直线慢慢生成的动画
    // .attr("stroke-dasharray", totalLen + " " + totalLen)
    // .attr("stroke-dashoffset", totalLen)
    // .transition()
    // .duration(this.lineAnimTime)
    // .ease("linear")
    // .attr("stroke-dashoffset", 0);

到这里,一般的坐标轴、饼图已经难不倒了,而且自己想添加什么就添加什么,无非就是计算x、坐标,d3就是一堆图形函数,帮你计算坐标而已,那还有什么新东西吗?假如UED说想要阴影,光效,这里介绍一下svg里面一个很牛叉的东西,filter

let defs = svg.append("defs");
let filter = defs.append("filter")
    .attr("id", "gaussianShadowFilter");
// append gaussian blur to filter
// filter.append("feMorphology")
//     .attr("operator", "dilate")
//     .attr("radius", 3);
filter.append( 'feGaussianBlur' )
    .attr( 'in', 'SourceAlpha' )
    .attr( 'stdDeviation', 2 ) // !!! important parameter - blur
    .attr( 'result', 'blur' );

// append offset filter to result of gaussion blur filter
filter.append( 'feOffset' )
    .attr( 'in', 'blur' )
    .attr( 'dx', 10 ) // !!! important parameter - x-offset
    .attr( 'dy', 1 ) // !!! important parameter - y-offset
    .attr( 'result', 'offsetBlur' );


// merge result with original image
let feMerge = filter.append( 'feMerge' );

// first layer result of blur and offset
feMerge.append( 'feMergeNode' )
    .attr( 'in", "offsetBlur' );

// original image on top
feMerge.append( 'feMergeNode' )
    .attr( 'in', 'SourceGraphic' );

实话说,这个东西我也只是到抄过来调参的地步,比如dx dy。理解上只知道特效输出为result,下一个特效的in是这个result,其他也是一问三不知,而且看了之后着实没有深入了解下去的想法,简直就是手写Photoshop。

下面是几个介绍filter的地址,留下以备不时之需

1.https://www.w3.org/TR/SVG/filters.html

2.https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/

3.https://jorgeatgu.github.io/svg-filters/

 

 

 

 

国内如何访问维基百科(Wikipedia)

目前,维基百科已经被墙了,但是上面毕竟有很多有用的东西。目前找到比较好用的办法,就是下载zim格式的维基百科的离线文件。

首先访问开源免费软件Kiwix所在的网页,地址为: http://wiki.kiwix.org/wiki/Main_Page/zh-cn 在这个网址中下载阅读软件,也可以在本站下载Kiwix-Windows, Kiwix-Mac

其次,下载维基百科对应语言的快照,zim格式的文件,目前中文语言文件的大小是10GB左右的样子。可以直接在 http://wiki.kiwix.org/wiki/Main_Page/zh-cn 这个网站中下载,也可以访问 https://dumps.wikimedia.org/ 在这网页中选择"Kiwix files"这部分的内容去下载。注意里面包含了几乎所有的语言,只需要选择对应的语言即可。

参考链接


chart.js 绘制雷达图

chart.js是一个很newblity的图标库,支持各种图(柱状、曲线、饼图、雷达图等等),使用起来也很简单,这里先奉上官方文档一份 http://www.bootcss.com/p/chart.js/docs/。

chart.js使用canvas绘制,缺点就是移动端性能有点差(或者是我入门太浅)。

官方文档介绍的不甚详细,这里给一个可以成功的example。

雷达图

1.引入chart.js

1).<script>标签引入    逼格太低

2).npm install chart.js 后,var Chart = require('chart.js')    逼格一般

3).npm install chart.js,使用gulp,import Chart from 'chart.js'  逼格目前最高

2.html文件中,一个canvas元素是必须的

<canvas id="myChart" style="width: 100%;height: 90%;outline: none"></canvas>

style比较简单,就不写css了

3.创建一个图标,只需要new一个chart对象就可以了

this.radarDom = document.getElementById("myChart");
var myNewChart = new Chart(this.radarDom.getContext("2d"), this.radarConfig());

需要两个参数,canvas的context,以及config

主要问题在于config,暂时未能完全了解

radarConfig() {
    var chartColors = {
        red: 'rgb(255, 99, 132)',
        orange: 'rgb(255, 159, 64)',
        yellow: 'rgb(255, 205, 86)',
        green: 'rgb(75, 192, 192)',
        blue: 'rgb(54, 162, 235)',
        purple: 'rgb(153, 102, 255)',
        grey: 'rgb(231,233,237)'
    };

    var color = Chart.helpers.color;
    var config = {
        // 图标类型
        type: 'radar',
        data: {
            // 雷达图的角
            labels: ["0:00", "3:00", "6:00", "9:00", "12:00", "15:00", "18:00", "21:00"],
            color: '#fff',
            datasets: [{
                // 雷达图颜色
                backgroundColor: color(chartColors.red).alpha(0.3).rgbString(),
                borderColor: chartColors.blue,
                pointBackgroundColor: chartColors.red,
                // 数据 如[1, 2, 3, 4, 5, 6, 7, 8]
                data: this.radarData
            }]
        },
        options: {
            fontColor:'#fff',
            custom: {
                fontColor: "#fff",
                color: "#fff"
            },
            legend: {
                display: false,
                position: 'left',
            },
            title: {
                display: true,
                text: '我的观影时段',
                fontColor: '#fff',
                fontSize: 24
            },
            scale: {
                // ticks 0 10 20等标签
                fontColor: '#ffffff',
                ticks: {
                    display: false,
                    beginAtZero: true,
                    fontColor: "#000",
                    maxTicksLimit: undefined,
                },
                //scaleLabel: {
                //    fontColor: "#000",
                //}
                // 线条
                gridLines: {
                    color: '#fff',
                    //zeroLineColor: '#fff'
                }
            }
        }
    };

    return config;
}

GitHub上的十一款热门开源安全工具

恶意软件分析、渗透测试、计算机取证——GitHub托管着一系列引人注目的安全工具、足以应对各类规模下计算环境的实际需求。

Stop Updates

GitHub上的十一款热门开源安全工具

作为开源开发领域的基石,“所有漏洞皆属浅表”已经成为一条著名的原则甚至是信条。作为广为人知的Linus定律,当讨论开源模式在安全方面的优势时,开放代码能够提高项目漏洞检测效率的理论也被IT专业人士们所普遍接受。

现在,随着GitHub等高人气代码共享站点的相继涌现,整个开源行业开始越来越多地帮助其它企业保护自己的代码与系统,并为其提供多种多样的安全工具与框架,旨在完成恶意软件分析、渗透测试、计算机取证以及其它同类任务。

以下十一个基本安全项目全部立足于GitHub。任何一位对安全代码及系统抱有兴趣的管理员都有必要对它们加以关注。

Stop Updates

Metasploit框架

作为由开源社区及安全企业Rapid7一手推动的项目,Metasploit框架是一套专门用于渗透测试的漏洞开发与交付系统。它的作用类似于一套 漏洞库,能够帮助管理人员通过定位弱点实现应用程序的安全性评估,并在攻击者发现这些弱点之前采取补救措施。它能够被用于对Windows、Linux、 Mac、Android、iOS以及其它多种系统平台进行测试。

“Metasploit为安全研究人员提供了一种途径,能够以相对普遍的格式对安全漏洞加以表达,”Rapid7公司工程技术经理Tod Beardsley指出。“我们针对全部设备类型打造出数千种模块——包括普通计算机、手机、路由器、交换机、工业控制系统以及嵌入式设备。我几乎想不出 有哪种软件或者固件无法发挥Metasploit的出色实用性。”

项目链接:https://github.com/rapid7/metasploit-framework

Stop Updates

Brakeman

Brakeman是一款专门面向Ruby on Rails应用程序的漏洞扫描工具,同时也针对程序中一部分数值向另一部分传递的流程执行数据流分析。用户无需安装整套应用程序堆栈即可使用该软件,Brakeman缔造者兼维护者Justin Collins解释道。

尽管速度表现还称不上无与伦比,但Brakeman在大型应用程序扫描方面只需数分钟、这样的成绩已经超越了“黑盒”扫描工具。虽然最近已经有针对 性地作出了修复,但用户在使用Brakeman时仍然需要留意误报状况。Brakeman应该被用于充当网站安全扫描工具。Collins目前还没有将其 拓展至其它平台的计划,不过他鼓励其他开发人员对项目代码作出改进。

项目链接:https://github.com/presidentbeef/brakeman

Stop Updates

Cuckoo Sandbox

Cuckoo Sandbox是一款自动化动态恶意软件分析系统,专门用于检查孤立环境当中的可疑文件。

“这套解决方案的主要目的是在启动于Windows虚拟机环境下之后,自动执行并监控任何给定恶意软件的异常活动。当执行流程结束之 后,Cuckoo会进一步分析收集到的数据并生成一份综合性报告,用于解释恶意软件的具体破坏能力,”项目创始人Claudio Guarnieri表示。

Cuckoo所造成的数据包括本地功能与Windows API调用追踪、被创建及被删除的文件副本以及分析机内存转储数据。用户可以对该项目的处理与报告机制进行定制,从而将报告内容生成为不同格式,包括 JSON与HTML。Cuckoo Sandbox已经于2010年开始成为谷歌代码之夏中的项目之一。

项目链接:https://github.com/cuckoobox/cuckoo

Stop Updates

Moloch

Moloch是一套可扩展式IPv4数据包捕捉、索引与数据库系统,能够作为简单的Web界面实现浏览、搜索与导出功能。它借助HTTPS与HTTP机制实现密码支持或者前端Apahce能力,而且无需取代原有IDS引擎。

该软件能够存储并检索标准PCAP格式下的所有网络流量,并能够被部署到多种系统之上、每秒流量处理能力也可扩展至数GB水平。项目组件包括捕捉、 执行单线程C语言应用程序、用户也可以在每台设备上运行多个捕捉进程;一套查看器,这实际是款Node.js应用程序、针对Web接口以及PCAP文件传 输;而Elasticsearch数据库技术则负责搜索类任务。

项目链接:https://github.com/aol/moloch

Stop Updates

MozDef: Mozilla防御平台

这款Mozilla防御平台,也就是MozDef,旨在以自动化方式处理安全事件流程,从而为防御者带来与攻击者相对等的能力:一套实时集成化平台,能够实现监控、反应、协作并改进相关保护功能,该项目缔造者Jeff Bryner解释称。

MozDef对传统SEIM(即安全信息与事件管理)功能作出扩展,使其具备了协同事件响应、可视化以及易于集成至其它企业级系统的能 力,Bryner指出。它采用Elasticsearch、Meteor以及MongoDB收集大量不同类型的数据,并能够根据用户需求以任意方式加以保 存。“大家可以将MozDef视为一套立足于Elasticsearch之上的SIEM层,能够带来安全事件响应任务流程,”Bryner表示。该项目于 2013年在Mozilla公司内部开始进行概念验证。

项目链接:https://github.com/jeffbryner/MozDef

Stop Updates

MIDAS

作为由Etsy与Facebook双方安全团队协作打造的产物,MIDAS是一套专门针对Mac设备的入侵检测分析系统框架(即Mac intrusion detection analysis systems,缩写为MIDASes)。这套模块框架提供辅助工具及示例模型,能够对OS X系统驻留机制中出现的修改活动进行检测。该项目基于《自制防御安全》与《攻击驱动防御》两份报告所阐述的相关概念。

“我们发布这套框架的共同目标在于促进这一领域的探讨热情,并为企业用户提供解决方案雏形、从而对OS X终端当中常见的漏洞利用与驻留模式加以检测,”Etsy与Facebook双方安全团队在一份说明文档中指出。MIDAS用户能够对模块的主机检查、验 证、分析以及其它针对性操作进行定义。

项目链接:https://github.com/etsy/MIDAS

Stop Updates

Bro

Bro网络分析框架“与大多数人所熟知的入侵检测机制存在着本质区别,”Bro项目首席开发者兼加州伯克利大学国际计算机科学协会高级研究员Robin Sommer指出。

尽管入侵检测系统通常能够切实匹配当前存在的各类攻击模式,但Bro是一种真正的编程语言,这使其相较于那些典型系统更为强大,Sommer表示。它能够帮助用户立足于高语义层级执行任务规划。

Bro的目标在于搜寻攻击活动并提供其背景信息与使用模式。它能够将网络中的各设备整理为可视化图形、深入网络流量当中并检查网络数据包;它还提供一套更具通用性的流量分析平台。

项目链接:https://github.com/bro/bro

Stop Updates

OS X Auditor

OS X Auditor是一款免费计算机取证工具,能够对运行系统之上或者需要分析的目标系统副本当中的伪迹进行解析与散列处理。包括内核扩展、系统与第三方代理 及后台程序、不适用的系统以及第三方启动项、用户下载文件外中已安装代理。用户的受隔离文件则可以提取自Safari历史记录、火狐浏览器 cookies、Chrome历史记录、社交与邮件账户以及受审计系统中的Wi-Fi访问点。

项目链接:https://github.com/jipegit/OSXAuditor

Stop Updates

The Sleuth Kit

The Sleuth Kit是一套库与多种命令行工具集合,旨在调查磁盘镜像,包括各分卷与文件系统数据。该套件还提供一款插件框架,允许用户添加更多模块以分析文件内容并建立自动化系统。

作为针对微软及Unix系统的工具组合,Sleuth Kit允许调查人员从镜像当中识别并恢复出事件响应过程中或者自生系统内的各类证据。在Sleuth Kit及其它工具之上充当用户界面方案的是Autopsy,这是一套数字化取证平台。“Autopsy更侧重于面向用户,”Sleuth Kit与Autopsy缔造者Brian Carrier指出。“The Sleuth Kit更像是一整套能够为大家纳入自有工具的库,只不过用户无需对该训加以直接使用。”

项目链接:https://github.com/sleuthkit/sleuthkit

Stop Updates

OSSEC

基于主机的入侵检测系统OSSEC能够实现日志分析、文件完整性检查、监控以及报警等功能,而且能够顺利与各种常见操作系统相对接,包括Linux、Mac OS X、Solaris、AIX以及Windows。

OSSEC旨在帮助企业用户满足合规性方面的各类要求,包括PCI与HIPAA,而且能够通过配置在其检测到未经授权的文件系统修改或者嵌入至软件 及定制应用日志文件的恶意活动时发出警报。一台中央管理服务器负责执行不同操作系统之间的策略管理任务。OSSEC项目由Trend Micro公司提供支持。

项目链接:https://github.com/ossec/ossec-hids

Stop Updates

PassiveDNS

PassiveDNS能够以被动方式收集DNS记录,从而实现事故处理辅助、网络安全监控以及数字取证等功能。该软件能够通过配置读取pcap(即数据包捕捉)文件并将DNS数据输出为日志文件或者提取来自特定接口的数据流量。

这款工具能够作用于IPv4与IPv6流量、在TCP与UDP基础上实现流量解析并通过缓存内存内DNS数据副本的方式在限制记录数据量的同时避免给取证工作带来任何负面影响。

项目链接:https://github.com/gamelinux/passivedns

中文翻译:GitHub上的十一款热门开源安全工具

英文原文:http://www.infoworld.com/slideshow/163151/11-open-source-security-tools-catching-fire-github-249652

Android-aidl传输parceble数据错误

写了一个service,使用AIDL跨进程调用时出现问题,service这边写的数据是正确的,client端拿到的是默认值,都是0.

找了下,

http://stackoverflow.com/questions/24615317/android-aidl-and-service-and-activity-parceable-seems-to-be-passing-default-clas

因为 in  out写反了。

in表示这个参数是入参,会被这个method读,但不会写。out意味着这个method会写但不读。inout表示会读也会写。

in/out/inout从字面意思上很好理解,要记住,它们是修饰参数,in/out都是相对于当前method的。

Android-获取内存,当前/总cpu信息

内存信息中最有用的就是PSS了,还有各种零碎的,在android.os.Debug中可以直接拿到MemeryInfo

 

MemoryInfo utInfo = new MemoryInfo();
android.os.Debug.getMemoryInfo(utInfo);

info.dalvikPrivateDirty = utInfo.dalvikPrivateDirty;
info.dalvikPss = utInfo.dalvikPss;
info.dalvikSharedDirty = utInfo.dalvikSharedDirty;
info.nativePrivateDirty = utInfo.nativePrivateDirty;
info.nativePss = utInfo.nativePss;
info.nativeSharedDirty = utInfo.nativeSharedDirty;
info.otherPss = utInfo.otherPss;
info.otherPrivateDirty = utInfo.otherPrivateDirty;
info.otherSharedDirty = utInfo.otherSharedDirty;
info.totalPss = utInfo.getTotalPss();

cpu信息分两类,总的cpu使用率,取proc/stat的数据,这个样子

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

取到之后解析,拿到使用时间和空闲时间

隔一段时间后,再取一次数据,即可得到cpu使用时间

private float readUsage() {
    try {
        RandomAccessFile reader = new RandomAccessFile("/proc/stat", "r");
        String load = reader.readLine();

        String[] toks = load.split(" ");

        long idle1 = Long.parseLong(toks[5]);
        long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
              + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

        try {
            Thread.sleep(360);
        } catch (Exception e) {}

        reader.seek(0);
        load = reader.readLine();
        reader.close();

        toks = load.split(" ");

        long idle2 = Long.parseLong(toks[5]);
        long cpu2 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
            + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

        return (float)(cpu2 - cpu1) / ((cpu2 + idle2) - (cpu1 + idle1));

    } catch (IOException ex) {
        ex.printStackTrace();
    }

    return 0;
}

当前进程的cpu使用率计算方法和总的相似,只不过路径变成了proc/<pid>/stat

Table 1-3: Contents of the stat files (as of 2.6.22-rc3)
..............................................................................
 Field          Content
  pid           process id
  tcomm         filename of the executable
  state         state (R is running, S is sleeping, D is sleeping in an
                uninterruptible wait, Z is zombie, T is traced or stopped)
  ppid          process id of the parent process
  pgrp          pgrp of the process
  sid           session id
  tty_nr        tty the process uses
  tty_pgrp      pgrp of the tty
  flags         task flags
  min_flt       number of minor faults
  cmin_flt      number of minor faults with child's
  maj_flt       number of major faults
  cmaj_flt      number of major faults with child's
  utime         user mode jiffies
  stime         kernel mode jiffies
  cutime        user mode jiffies with child's
  cstime        kernel mode jiffies with child's

解析数据代码如下,如果不计算子进程时间则只用toks[13]和toks[14]

reader = new RandomAccessFile("/proc/" + android.os.Process.myPid() + "/stat", "r");
String currentStat = reader.readLine();
toks = currentStat.split(" ");
// include child  or (13 + 14)
current_process_usetime = Long.parseLong(toks[13]) + Long.parseLong(toks[14]) + Long.parseLong(toks[15])
        + Long.parseLong(toks[16]);
reader.close();

取到之后除以总的cpu时间,即

long totalCPUTime = (cpu2 + idle2) - (cpu1 + idle1);
currentCPUUsage = (float)(current_process_usetime - last_process_usetime) / totalCPUTime;

通过jni获得应用Icon(getApplicationIcon)

jni获得应用icon有两步

1.在java端写一个方法,通过包名获取appIcon,返回byte[]

public static byte[] getAppIcon(String packageName) {
        BzDebugLog.i(TAG, "getAppIcon, " + packageName);
        try {
            PackageManager pm = BzAppConfig.context.getContext().getApplicationContext().getPackageManager();
            Drawable iconDrawable = pm.getApplicationIcon(packageName);
            Bitmap my_bitmap = ((BitmapDrawable) iconDrawable).getBitmap();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            my_bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
            byte[] b = stream.toByteArray();
//            String encodedString = new String(b);
            return stream.toByteArray();
        } catch (Exception e) {
            BzDebugLog.e(TAG, "getAppIcon error: " + e.toString());
            return null;
        }
    }

native初始化时,获取这个method,同时拿到VM和全局context

jobject mBzAppContext;
JavaVM *m_appContext_VM = NULL;

mAppContext = env->NewGlobalRef(thiz);
env->GetJavaVM(&m_appContext_VM);

FIND_CLASS(clazz, "com/xx/xx/xx");
if (clazz == NULL) {
loge("class not exist!!");
}

GET_METHOD_ID(gAppGlobalCallMethod.getAppIcon, clazz,
"getAppIcon", "(Ljava/lang/String;)[B");
if (gAppGlobalCallMethod.getAppIcon == NULL) {
loge("method get fail!!");
}

(Ljava/lang/String;)[B 指入参是String,回参是jbyteArray

2.调用jni函数,拿到byte[],转成char*

void readAppIcon(const char* packageName)
{
if (m_appContext_VM == NULL) {
loge("m_appContext_VM null");
return;
}
if (mAppContext == NULL) {
loge("mAppContext null");
return;
}
if (gAppGlobalCallMethod.getAppIcon== NULL) {
loge("gAppGlobalCallMethod.getAppIcon null");
return;
}
CHECK_JVM_THREAD(m_appContext_VM);
if (error) {
loge("check jvm thread error");
return;
}
jstring jPackageName = env->NewStringUTF(packageName);
jbyteArray jbarray = (jbyteArray)env->CallObjectMethod(mBzAppContext, gAppGlobalCallMethod.getAppIcon, jPackageName);

if (jbarray == nullptr) {
logi("getAppIcon null");
} else {
len = (int)env->GetArrayLength (jbarray);
char *data = (char*)env->GetByteArrayElements(jbarray, 0);
// 这里就把appicon拿到并转成了char*,注意释放data

env->ReleaseByteArrayElements(jbarray, data, 0);
}
if (attached) {
m_appContext_VM->DetachCurrentThread();
}
}

 

AndroidStudio 更改jar包编译顺序优先于android.jar,使用@hide api

使用@hide 的api,可以通过反射,但反射速度慢,且使用不方便。

@hide的api,在编译的时候有问题,如果能编译成功,是可以调用的。那么问题来了,如何编译成功?

可以修改android源码,去掉@hide,编译生成该模块的framework.jar。Eclipse中直接引入该jar包在android.jar前即可。AndroidStudio就比较麻烦,需要做两步。

1.引入jar包,这个网上有无数方法,不表。注意设置compile->provide,gradle文件如下:

provided files('lib/android_packageManager.jar')

原因就是我们只需要编译成功,不需要把这个包打到系统。

这时候在系统自动生成的iml文件中,可以看到一排orderEntry标签 <orderEntry>,也就是我们引用的所有jar包信息,把framework的jar包放到orderEntry的第一位,也就是

<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />

前面,这时候IDE就不报错了。

不过只是表象,方便我们写代码自动补全而已。而且iml文件是自动生成的,每次重启IDE都要重新改一下。

2.真正的编译需要在工程的gradle中增加编译选项

找到

allprojects {


    repositories {
     jcenter()
    }
}

在repositores同级增加

gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
        options.compilerArgs.add('-Xbootclasspath/p:/lib/android_packageManager.jar')
    }
}

Xbootclasspath/p:是Java编译的寻址优先设置,先找缺省路径还是全路径

Xbootclasspath/p:

不确定路径怎么填的,可以使用绝对路径,如

Xbootclasspath/p:D://mProject/app/lib/android_packageManager.jar')

编译通过

Tips:

1.有时候@hide的api编译过了,其他地方又报错。查看一下报错的api,看看是不是对android版本有要求,framework.jar编译最好用较新的编译环境。

2.不要做过多的其他尝试,这两步绝对可以解决问题。