Salesforce怎么从生产环境删除Apex

一般来讲,就算是DevOps团队也很难遇到需要删除Apex的情景。
毕竟在一个系统的生命周期里,大部分时间都是不断的增加功能,或者改动现有功能。
仅剩的一点点的遗弃功能的需求,也会因为考虑到风险问题,从而选择将废弃代码放在那里不再调用。
毕竟,维护团队的准则————不动就没事,动了就要担风险。

其实,还有一种需要删除Apex操作的情景,就是回滚。
Salesforce与常规的Java项目不大一样,如果上线出现问题,很难做到完美回滚。
曾经有人很天真地和我说,将生产环境的MetaData全部备份,如果上线出问题就全都deploy回去。
java web系统确实可以这么做,一个新版war包,一个旧war包,出问题就停机覆盖回去,一切就都恢复如初了,完美。
可惜,如果Salesforce这么做就会面临一场巨大的灾难!
首先,你有很大的几率会彻彻底底的失败。因为Salesforce在deploy的时候要计算metadata之间的关系。
你会因为各种狗追尾巴似的关系依赖,导致失败,继而崩溃。
还有一些永远无法被正确Deploy上去的metadata,会将你的生产环境毁成一片废墟。
在Salesforce里,只要是稍微复杂一点的Org,就永远不要幻想将全部的Metadata都一口气deploy上去。

话说回来,Salesforce最安全的回滚方式还是逆操作。就像录像带的倒放。
但是,就算是逆操作,仍然不能保证安全的,原原本本的回到从前,尤其是回滚包含修复数据动作的时候。
所以在上线之前,针对回滚,认认真真地制定一个回滚方案是非常,非常有必要的。
当然,如果你每次上线只是加一个Field,当我没说。
不过,就算是只删一个Field的话,慎重吧。

回到正题,怎么去删除一个Apex。
有的同学说了。直接删就好了啊。
那么。。。只接触过Developer环境和Sandbox的可以出门右转了。生产环境是无法直接删除的。

那么,下面,先讲一个简单的方法。

首先,我们有一个ApexClass,叫DeleteSample。

然后在ForceIDE中,新建一个Project,将其MetaData取下。

之后打开此Apex,之后切换到Metadata标签上。

然后将Status从Active改成Deleted

然后,保存,在Apex上点右键,Force.com -> Deploy to Server。

之后填入用户名密码,下一步,强调一下,被删除的Apex会被标记成Overwrite,且默认不被选中。如果是新建的代码则会被标记成New,绿色,自动勾选。修改的代码被标记成Modify,黄色,自动勾选。这里,一定要记得钩选上。

然后无脑下一步,直到看到Success。

然后回到系统里,就发现DeleteSample已经不见了!

就这样。简单吧。

 

Salesforce用Apex判断Role Hierarchy的简单代码示例

(上密码遭人恨。。。。先开放好了)

由于role不同于Profile,带有阶层性质,所以有一些自定义功能要依赖于这种阶层的设定。这样就涉及到role hierarchy的判断问题。

我是一个绝懒之人,所以去网上搜了一下,能找到的方案都或多或少有些缺陷 。

我所提供的方案也是如此,但是想比于浪费太多SOQL查询次数来讲,role的数量不超过50000条已经是足够好了。
// 这里Update一下,其实根本不会有那么多的Role,因为默认500,提票才能达到10000。

Talk is cheap, show you the code.
PS:最近正在建设个人代码库,本身也只是一个简单的示例,之后会放出完全体版本。
也许也会放到Github上。

// @Version 0.1 Author Keal. Email: [email protected]
// @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
public class RoleHierarchyHelper {
	Boolean isEnd = false;
	Boolean compareResult = false;
	public static List<UserRole> roleList = [SELECT Id, Name, DeveloperName, ParentRoleId FROM UserRole LIMIT 50000];
	
	public RoleHierarchyHelper() {
		// pretend Salesforce spasms LOL. It happened.
		if(roleList == null || roleList.size() == 0) {
			roleList = [SELECT Id, Name, DeveloperName, ParentRoleId FROM UserRole LIMIT 50000];			
		} else {
			system.debug('=======RoleList.size()========' + roleList.size());
		}
	}
	
    public Boolean isSubordinate(String currentRoleId, String compareRoleId){
    			
	    List<UserRole> childRoleList = new List<UserRole>();
	    Map<Id, UserRole> childRoleMap = new Map<Id, UserRole>();
	    Set<Id> exeUserRoleIdSet = new Set<Id>();
	    exeUserRoleIdSet.add(currentRoleId);
	    
	    isEnd = false;
	    compareResult = false;
    	while(!isEnd) {
			childRoleMap = new Map<Id, UserRole>();
	    	for(UserRole ur: roleList) {
	    		if(ur.ParentRoleId != null && exeUserRoleIdSet.contains(ur.ParentRoleId)) {
	    			childRoleMap.put(ur.Id, ur);
	    		}
	    	}
	    	
			if(childRoleMap.size() > 0) {
				if(childRoleMap.containsKey(compareRoleId)) {
					isEnd = true;
					compareResult = true;	
				} else {
					isEnd = false;
					exeUserRoleIdSet = new Set<Id>();
					exeUserRoleIdSet.addAll(childRoleMap.keyset());			
				}
				
			} else {
				isEnd = true;
				compareResult = false;
			}
    	}
    	return compareResult;
    }
    
    public Boolean isSuperior(String currentRoleId, String compareRoleId) {
    	Map<Id, UserRole> idUserRoleMap = new Map<Id, UserRole>(roleList);
    	Id exeUserRoleId = currentRoleId;
    	UserRole exeUserRole = new UserRole();
    	
	    isEnd = false;
	    compareResult = false;
    	while(!isEnd) {
    		exeUserRole = idUserRoleMap.get(exeUserRoleId);
    		if(exeUserRole.parentRoleId == null) {
    			isEnd = true;
    			compareResult = false;
    		} else {
    			if(exeUserRole.parentRoleId == compareRoleId) {
	    			isEnd = true;
	    			compareResult = true;
    			} else {
	    			isEnd = false;
	    			exeUserRoleId = exeUserRole.parentRoleId;
    			}
    		}
    	}
    	return compareResult;
    }
    // TODO isSameLine()
}

搬瓦工启用chacha20加密算法的方法

搬瓦工的一键SS默认的加密算法是AES-256,这个算法在当时算是兼具了安全和性能的不二选择。

可是随着时代的发展,AES-256的性能已经不能满足广大人民群众的需求。所以新一代的算法冉冉升起————–chacha20

chacha20虽然名字很奇怪,但确是Google出品。来自RFCWikipedia的介绍。

ChaCha20

Google选择了带有Bernstein的Poly1305消息认证码的ChaCha20作为一个OpenSSL中RC4的替代品,用以完成互联网的安全通信。[17]Google最初实现了https (TLS/SSL)流量在Chrome浏览器(Android手机版)与Google网站之间的通信。[18]
不久之后,Google在TLS中采用它,ChaCha20和Poly1305算法也以 [email protected] 成为OpenSSH中的一个新密码包。[19][20]后来,通过编译时选项避免它依赖于OpenSSL也成为可能。[21]
ChaCha20也被用在OpenBSD[22]和NetBSD[23]操作系统中的arc4random随机数生成器,替换已经脆弱的RC4,在DragonFly BSD[24]中内核的CSPRNG子程序中也是如此。[25][26]
ChaCha20已经在RFC 7539中标准化。它在IKE和IPsec中的使用已在RFC 7634中标准化。在 RFC7905中,Chacha20-Poly1305 已经被加入 TLS 扩展标准([1])。

chacha20的优势在于移动设备的性能。所以我们开始动手改造吧。

按照以下步骤:

首先SSH连接自己的搬瓦工服务器。
标准起手动作,yum update。
然后执行下面命令。

yum install m2crypto gcc -y
wget https://github.com/jedisct1/libsodium/releases/download/1.0.8/libsodium-1.0.8.tar.gz 
tar zfvx libsodium-1.0.8.tar.gz
cd libsodium-1.0.8
./configure
make && make install
echo "include ld.so.conf.d/*.conf" > /etc/ld.so.conf
echo "/lib" >> /etc/ld.so.conf
echo "/usr/lib64" >> /etc/ld.so.conf
echo "/usr/local/lib" >> /etc/ld.so.conf
ldconfig

因为我还没找到搬瓦工自带SS的配置文件位置。所以只能用临时的办法。

ps -ef | grep ssserver
// 获得 sssever PID
kill [PID]
/usr/bin/ssserver -p [your port] -k [your password] -m chacha20 --user nobody  

之后在客户端也选择chacha20就OK了。

操作完之后,虽然搬瓦工自带的SS管理页面看起来是STOP状态,但实际上服务器已经重新启动了。
等找到配置文件的位置之后,再更新比较完善的方法。

// Update 1 2017-02-08

关于如何做到开机自动启动与自定义参数的问题,已经找到了方案。
首先,在/etc目录下创建配置文件shadowsocks.json(vi /etc/shadowsocks.json或者ftp等你喜爱的方式)
然后输入下列内容

{
"server":"[Server IP]",
"local_address":"127.0.0.1",
"local_port":1080,
"port_password":{
"[Port]":"[Password]"
},
"timeout":300,
"method":"chacha20",
"fast_open":false
}

然后,打开/etc/rc.local,将原来的ssserver启动命令删掉,然后写入

ssserver-c /etc/shadowsocks.json -d start

之后重启,连接成功就OK了。
以后想改密码或者端口的话,直接改/etc/shadowsocks.json中的设置,重启就好。

// Update 2 2017-06-18
今天发现所有使用chacha20加密的流量被服务器疯狂的拒绝。然后监视服务器侧并没有收到任何请求,推测已被拦截。
为了追求速度快就会留下马脚。所以请暂且使用AES-256吧

// Update 2 2017-09-08
更新了tar包的url地址

如何在Salesforce中使用匿名块建立每小时执行一次的Scheduler

在Salesforce中建立Scheduler的方式有两种

  1. 在Develop->Apex Classes页面,点击Schedule Apex按钮之后会提供如下的面板。

    这个面板虽然能方便的决定Scheduler执行的频率,但是最高频率也只能是一天一次。
  2. 再有就是使用Apex。在匿名块中使用系统函数设定Scheduler,可以获得更高的频率。

所以,如果想设定一小时执行的Scheduler就必须使用第二种方式。
需要使用的函数为System.schedule();
官方文档对此进行了详细的描述。具体参照下列示例代码。

// Sample 1 hour 1 time
System.schedule('My Scheduler','0 0 0/1 * * ?', new MyScheduler()); 

作为一个成熟的完善的系统,定时器与后台Job是必须的组成部分。
Scheduler就是Linux Cron的强力山寨。
所以System.schedule()中的第二个参数,就等同于Cron表达式(当然,只能算一个子集)。

为什么要把一小时一次单独拎出来,因为,这是Scheduler的最小粒度。
Scheduler表达式的秒位与分位是不支持“/”符号的。

所以,想几分钟执行一次的,放弃吧。

匿名块中SavePoint的使用技巧

经常要使用匿名块验证代码片段的可用性,尤其是在写测试类的时候。一个很长的测试类,一口气写完,再逐一排查问题是很低效的。

但是,使用匿名块执行代码片段的话,插入数据或者修改数据的动作就会对数据库造成实际的影响。这个时候,我们就可以使用SavePoint来避免这个问题。

如下

Savepoint sp = Database.setSavepoint();

// Place your code here

Database.rollback(sp);

这样,如果DML代码成功了,也不会对数据库造成任何影响。

关于SavePoint和Rollback,参照官方文档

CentOS搭建Java Web环境

题外话:首先痛斥国内“技术”博客圈的拿来风。用汉语搜索到的基本上都是重复的内容,如果最开始的人写错了,那么所有人一起错。或者明明第一篇文章是五年前写出来的,技术环境已经都废弃了,结果到现在还有人转。连最基本的验证都不做。

下面进入正题:
全世界大部分的网站都是PHP做成的。所以各大VPS供应商都提供了一键PHP安装环境。或者各个社区也提供了方便的一键PHP环境安装脚本。相比之下,JAVA就没这么好运了。

之前买了一个搬瓦工的VPS想部署JAVA代码。结果一直被误导。今日虽然已经深夜,仍然打算将此过程记下来。
记住,做成时间2017/01/15。时间太长之后就别参照了。一切都在变。

下面是步骤。

  1. 进入搬瓦工后台,选择系统,CentOS7 64位。 等待重装完毕。
  2. 用Putty进入服务器后台。SSH的端口和密码在重装系统时会自动提供。
  3. 首先安装Java,使用 yum install java即可。一路y,默认安装的应该是OpenJDK(Oracle在Do evil)。
  4. 然后执行java -version查看java版本,以此确认java安装是否正常。现在为OpenJDK1.8。
  5. 之后安装Tomcat。执行yum install tomcat tomcat-webapps tomcat-admin-webapps。网上文章最坑人的地方在于,都是写Tomcat6。其实早就不带版本号了。
  6. 然后一路Y。
  7. 去etc/tomcat路径下面去修改各配置文件。比如Server.xml,tomcat-users.xml,web.xml等。
  8. webapps路径会在/var/lib/tomcat/webapps。根目录是ROOT。
  9. 之后执行service tomcat start会启动服务。
  10. 执行service tomcat stop停止服务。
  11. 执行service tomcat restart重启服务。

//TODO 设置服务自启动。
//TODO 补图。

// Update 1

补充MacOS下使用终端连接SSH的方法。
网上的教程一般指告诉到了使用ssh [userName]@[Host]为止,但是,正常的服务器是不会使用默认端口号的。
那么怎么指定端口号呢,完整版命令是这样的ssh [UserName]:@[Host] -p [Port]

// Update 2

在Win10的最新版本里,可以安装Beta版的Ubuntu Linux,这样不需要Putty等工具,直接在Win10里使用SSH命令了。
具体命令和MacOS里是一样的。

Javascript如何生成Json

最近遇到个想复杂了的问题,就是如何用Javascript生成Json。
因为之前用的都是高级语言,已经封装好了数据结构或者类,直接赋值就好了。
之前想模拟前端向后台呼叫接口,需要POST方式传递JSON进来。然后就懵住了。

咦?我应该不用傻傻的拼接字符串吧。然后开始了脑洞。

脑洞第一版,模仿Salesforce制作List的方式

var dataArray = [];
var data = {};
data["id"] = "0010000000ABCD";
data["name"] = "Account";
dataArray.push(data);
var jsonString = JSON.stringify(dataArray);
var output = eval("(" + jsonString + ")");

感觉很怪。

脑洞第二版,用Javascript的类概念

var Account = new Object();
Account.Id = "0010000000ABCD";
Account.Name = "Account";
var output = JSON.stringify(Account);

感觉好多了,但感觉还是不够简单。毕竟Json是Javascirpt原生支持的啊。

然后查了半天,发现自己真的想复杂了

var output = {Id:"0010000000ABCD", Name:"Account"};

// Update 1
补充一下如何传两条数据

var output = [{Id:"0010000000ABCD", Name:"Account1"}, {Id:"0010000000ABCE", Name:"Account2"}];

Google的人工智能

在围棋领域,Google的Alpha GO与Master已经打败了光荣的人类棋手们。很了不起。

不同与早先的下棋智能程序,Google的AI并没有使用遍历算法。早先红白机上的中国象棋之类的,不过是计算了所有的可能之后回应。围棋的变化太多,计算量超乎目前计算机的承受能力。

Google使用了深度学习系统Tensorflow。(Python,又是Python。。。这个世界要被Python占领了吗)

这里是来自极客学院的翻译教程

关于随机数

在随机数这个问题上,一般程序员总是要经历下面的过程:

  1. 压根没有随机数的概念,想使用随机数的念头往往出于想每次执行自己辛辛苦苦写的小程序时可以有不一样的结果。
  2. 发现有个函数/方法是专门用来生成随机数的,试了一下,确实挺随机的。
  3. 兴高采烈的在自己的小程序里使用了这个函数/方法。结果发现每次结果其实还是一样的(C的话)
  4. 经历了千万次google才发现原来自己没加种子,只有不同的种子才会产生不同的随机数。往往种子都是使用一个恒定在变的东西——–时间。
  5. 当程序被高频访问的时候,问题又来了————-时间在计算里有最小单位,所以高频访问中临近访问的随机数是一样的。
  6. 又学会了一种叫随机数仓库的东西,来应对突然的高频访问。不断的往随机数队列里填充随机数,然后每个访问都是去随机数队列里去取。
  7. 然后,突然有一天,有人告诉你,这些不是随机数,最多只能称为伪随机数。

那么问题来了,什么是伪随机数,维基百科上的解释

伪随机性英语:Pseudorandomness)是指一个过程似乎是随机的,但实际上并不是。例如伪随机数(或称伪乱数),是使用一个确定性的算法计算出来的似乎是随机的数序,因此伪随机数实际上并不随机。在计算伪随机数时假如使用的开始值不变的话,那么伪随机数的数序也不变。伪随机数的随机性可以用它的统计特性来衡量,其主要特征是每个数出现的可能性和它出现时与数序中其它数的关系。伪随机数的优点是它的计算比较简单,而且只使用少数数值很难推算出计算它的算法。一般人们使用一个假的随机数,比如电脑上的时间作为计算伪随机数的开始值。

所以严格来讲,计算机生成的随机数并不是严格意义上的随机。那么我们如果想要真正的随机数怎么办呢。

有一个简便的方法,调用http://random.org的接口。

调用示例等参考此链接

 

Javascript的KeyCode

在前端开发中,经常使用onkeypress,onkeydown等event来做快捷键。
我之前就是打算使用“;”来做快捷键功能。
使用了如下代码

document.onkeydown = function(e) {
    e = e || window.event;
    var kcode = e.which || e.keyCode;
	if(String.fromCharCode(kcode) == ";") {
              // TODO Place your code here.
	}
};

结果。。。只有在Firefox好用,Chrome和IE都阵亡了。
我并没有做任何的浏览器判断,怎么会自动按浏览器区分功能呢?

原来,对于event的keycode,大部分的按键的keycode值在所有浏览器中都是一致的。
但是,有一小撮按键。。。。很不幸就有我选中的的“;”,在firefox和Chrome中是不一致的。
具体列表参照http://www.javascripter.net/faq/keycodes.htm