在 orgmode 中粘贴截图

用 org2blog 写博客有一段时间了,但是从来都不在文章中加配图。因为一直没有找到好的办法,可以在 orgmode 中粘贴并管理图片。

这个问题还是要解决的,在网上搜了一圈解决方案。一个办法是使用命令行截图软件,将图片保存到指定位置,然后在 orgmode 中插入路径。我平时使用 picpick 这个截图软件,不想更换其它软件,所以不采用这个方案。

最后在 stackoverflow 上找到一个方法, 先用 C# 写一个小程序,从剪切板中将图片保存到指定路径,然后在 orgmode 中插入这个路径。这个方法可以与 picpick 软件可以配合使用,感觉不错,决定试一下。

  • 用 VS2005 新建一个 C# Windows Program 项目,命名为 CbImage2File
  • 删除 Form1.cs
  • 修改 program.cs 文件
using System;
using System.Collections.Generic;
using System.Text;

namespace CbImage2File
{
    static class Program
    {
        private static int FAILURE = 1;

        private static void Failure(string description)
        {
            Console.Error.WriteLine("Clipboard does not contain image data");
            Environment.Exit(FAILURE);
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            System.Drawing.Image image = System.Windows.Forms.Clipboard.GetImage();

            if (image == null)
            {
                Failure("Clipboard does not contain image data");
            }

            if (args.Length == 0)
            {
                Failure("You have not specified an output path for the image");
            }

            if (args.Length > 1)
            {
                Failure("You must only specify a file path (1 argument)");
            }

            string filePath = args[0];
            System.IO.DirectoryInfo folderInfo = new System.IO.FileInfo(filePath).Directory;

            if (!folderInfo.Exists)
            {
                System.IO.Directory.CreateDirectory(folderInfo.FullName);
            }

            image.Save(filePath);
        }
    }
}
  • 编译 release 版本,得到 CbImage2File.exe
  • myconfig/package.el 中增加
;;;###autoload
(defun org-insert-image-from-clipboard ()
  (interactive)
  (let* ((home-dir "d:/cygwin/home/fj/")
         (relative-dir "org/image")
         (attachments-dir (concat home-dir relative-dir))
         (png-file-name (format-time-string "%Y%m%d_%H%M%S.png"))
         (png-path (concat attachments-dir "/" png-file-name))
         (relative-path (concat relative-dir "/" png-file-name))
         (temp-buffer-name "CbImage2File-buffer"))
    (call-process "D:/tools/CbImage2File.exe" nil temp-buffer-name nil png-path)
    (let ((result (with-current-buffer temp-buffer-name (buffer-string))))
      (progn
        (kill-buffer temp-buffer-name)
        (if (string= result "")
            (progn 
              (insert (concat "[[~/" relative-path "]]"))
              ;(org-display-inline-images)
              )
          (insert result))))))

测试图片

图1  这是一张测试图片

spring 入门之 spring-data-jpa

在 IDEA 中,新建工程

  • 工程类型为 maven
  • 选中 create from archetype ,选择 quickstart 模板
  • 填写项目名称,完成创建

pom.xml 中添加 spring-data-jpa 依赖

  • jpa vendor 使用 hibernate
  • 数据库使用 H2
<groupId>org.springframework.data</groupId>
  <artifactId>spring-data-jpa</artifactId>
  <version>1.11.1.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.2.8.Final</version>
</dependency>

<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>1.4.193</version>
</dependency>

创建 spring context 和数据源

resources/spring-beans.xml

  • 文件名任意,需要在 main 函数中指定该文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
  • 自动生成的 beans 标签可能会出错,需要仔细检查。
  • 创建相关 bean,按依赖关系排序

    DataSource: 指定数据源为 schema.sql
    JpaVendorAdapter: 实例化 HibernateJpaVendorAdapter, 指定数据库类型为 H2
    EntityManagerFactory: 依赖 DatasSource, JpaVendorAdapter
    TransactionManager: 依赖 EntityManagerFactory
    
  • context:component-scan= 指定扫描注解的包路径
  • jpa:repositories 指定 DAO 包路径, 依赖 EntityManagerFactory 和 TransactionManager
  • tx:annotation-driven 指定 transaction 管理器
  • 最后新建 service bean, 在 main 函数中通过 context.getBean 得到该 bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/data/jpa
       http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
       http://www.springframework.org/schema/jdbc
       http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

  <context:component-scan base-package="com.litchi"/>

  <bean id="transactionManager" 
        class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="emf"/>
  <tx:annotation-driven transaction-manager="transactionManager" />

  <bean id="jpaVendorAdapter" 
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="H2"/>
    <property name="showSql" value="true"/>
    <property name="generateDdl" value="false"/>
    <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect"/>
  </bean>

  <jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="schema.sql"/>
  </jdbc:embedded-database>

  <bean id="emf" 
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        p:dataSource-ref="dataSource"
        p:jpaVendorAdapter-ref="jpaVendorAdapter"
        p:packagesToScan="com.litchi"/>

  <jpa:repositories base-package="com.litchi"
                    entity-manager-factory-ref="emf"
                    transaction-manager-ref="transactionManager">
  </jpa:repositories>

  <bean id="personService" class="com.litchi.PersonService"/>
</beans>

resources/schema.sql

drop table if exists Person;
create table Person (
  id identity,
  firstName varchar(25) not null,
);

新建代码文件

实体类 com.litchi.Person

@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String firstName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

DAO 类 com.litchi.PersonDao

@Repository
public interface PersonDao extends JpaRepository<Person, Integer> {
    Person save(Person person);

    List<Person> findAll();
}

Service 类 com.litchi.PersonService

@Service
public class PersonService {
    @Autowired(/*required = true*/)
    private PersonDao personDao;

    @Transactional
    public void createPerson(String name) {
        Person person = new Person();
        person.setFirstName(name);
        personDao.save(person);
    }

    public void PrintAllPerson() {
        List<Person> persons = personDao.findAll();
        System.out.println("Count: " + persons.size());
        for (Person p : persons) {
            System.out.println(p.getId() + " : " + p.getFirstName());
        }
    }

}

Main 函数

public static void main( String[] args )
{
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
    PersonService service = (PersonService) context.getBean("personService");
    service.createPerson("FengJian");
    service.PrintAllPerson();
    System.out.println( "Hello World!" );
}

新建运行配置

  • 运行类型为 Application
  • Main class 为 com.litchi.App

spring 入门之 spring-mvc + Tiles

spring-mvc 入门之 Hello World! 基础上添加 tiles 内容。

添加 POM 依赖

<dependency>
  <groupId>org.apache.tiles</groupId>
  <artifactId>tiles-servlet</artifactId>
  <version>3.0.7</version>
</dependency>

<dependency>
  <groupId>org.apache.tiles</groupId>
  <artifactId>tiles-jsp</artifactId>
  <version>3.0.7</version>
</dependency>

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

修改 spring01-servlet.xml

  • 删除 InternalResourceViewResolver 定义的 bean
  • 添加 tiles 配置器和解析器
<bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
  <property name="definitions">
    <list>
      <value>/WEB-INF/views/views.xml</value>
    </list>
  </property>
</bean>

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass">
    <value>org.springframework.web.servlet.view.tiles3.TilesView</value>
  </property>
</bean>

添加 tiles 模板

新建 WEB-INF/views 目录

新建 views.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
    "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>
  <definition name="template" template="/WEB-INF/views/template.jsp">
    <put-attribute name="header" value="/WEB-INF/views/header.jsp"/>
  </definition>

  <definition name="main" extends="template">
    <put-attribute name="content" value="/WEB-INF/views/content.jsp"/>
  </definition>
</tiles-definitions>

新建 jsp 模板文件

  • template.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>

<html>
<head>
  <title>Template</title>
</head>
<body>
  <tiles:insertAttribute name="header"/>
  <tiles:insertAttribute name="content"/>
</body>
</html>
  • 新建 header.jsp
<h1>this is header</h1>
  • content.jsp
<%@ page isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<p>Hello1, ${name} </p>
<p>Hello2, <c:out value="${name}"/> </p>

spring 入门之 spring-mvc

在 IDEA 中,新建工程

  • 工程类型为 maven
  • 选中 create from archetype ,选择 webapp 模板
  • 填写项目名称,完成创建

pom.xml 中添加 spring-mvc 依赖

  • 只要填写 artifactId。IDEA 自动完成 groupId,列出本地 maven 仓库中已有版本号。如果需要,可以手工将版本号修改成最新。
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>4.3.7.RELEASE</version>
</dependency>

修改 web.xml ,增加 spring-mvc servlet

<servlet>
  <servlet-name>spring01</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>spring01</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

新建 spring servlet 配置文件

  • 通过右键在 WEB-INF 目录下新建文件,类型选择 spring config
  • 文件名为 spring01-servlet.xml ,要与之前的 servlet-name 匹配
  • IDEA 自动完成 beans 标签
  • 注意:如果运行时提示找不到 sping-cache 库,可能是 beans 标签中的 spring-mvc 自动写成了 spring-cache ,需要手工修改。否则需要添加 POM 依赖。
<mvc:annotation-driven/>
<context:component-scan base-package="com.litchi"/>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/"/>
  <property name="suffix" value=".jsp"/>
</bean>

新建 controller 类

  • 在 main 目录下新建 java 目录
  • 右键 Make directory as -> Source Root
  • 新建 MyController.java
@Controller
public class MyController {
  @RequestMapping("home")
  public String HomePage(ModelMap model) {
    return "home";
  }

新建运行配置

  • 服务器选择 tomcat7
  • 添加 deplyment,选择 exploded 版本

从 Ubuntu 14.04 LTS 升级到 Ubuntu 16.04 LTS

LTS 是 Ubuntu 长期维护版本,时间为五年。维护时间到期后,软件不再更新,如果安装某个软件最新版本,可能会导致各种不兼容。当前使用的是 14.04LTS,下一个稳定长期维护版本是 16.04LTS。Ubuntu 的升级可不像商业操作系统升级这么省心,系统的升级带来软件的升级,而软件的升级并不保证配置的兼容性。

选择一个合适的升级时间点,也有讲究。如果 16.04LTS 刚发布就升级,对于升级中碰到的各种问题,网上很难找到解决方案;如果拖到版本维护时间快到期时再升级,又会导致不兼容的软件变多,需要解决更多的升级问题。所以我选择 16.04LTS 发布一年左右这个时间点进行升级。

  • 首先应该阅读 linode 上的 Ubuntu 16.04 LTS 升级指导。这里主要强调几点:
    • 软件升级到最新 apt-get update && apt-get upgrade
    • 在 linode 管理界面上选择最新内核
    • 安装升级管理软件 apt-get install update-manager-core
    • 备份当前系统

      我的方法是新建一个 linode 节点,并 clone 当前节点。升级完成,解决所有问题后,再删除此节点。linode 节点是按时间收费的,这个节点存在的时间很短,花费很少。

    • 停止重要系统服务
    • 升级 do-release-upgrade
  • 升级过程中,一直有交互提示信息。比如:询问保留配置还是覆盖配置;询问是否升级数据库;询问是否删除无用的软件包,等等。 这个过程要小心谨慎,一不小心你的配置就会被删除。
    • 提示是否打开新的 sshd 进程和端口,因为当前 ssh 可能会中断。选择是。
    • 是否更新源地址到最新版本。选择是。
    • 是否下载并安装 16.04LTS 对应的新版本软件,有些软件没有对应候选版本,无法升级。选择是。
    • 是否删除 sysstat 日志。选择是。
    • phpmyadmin 升级相关问题。我升级失败,选择 abort。在新版本里删除重装就行了。
    • 是否覆盖 /etc/nginx/nginx.conf 。选择是。记得在新系统中修改。
    • 是否安装新版本配置 20-imap.conf 。选择是。
    • 是否自动重启服务。选择是。
    • 是否覆盖 /etc/ppp/options 。选择是。
    • 是否覆盖 /etc/vsftpd.conf 。选择是。记得在新系统中修改。
    • 是否覆盖 50unattended-upgrades.ucftmp 。选择是。
    • 升级完成后,提示 pptpd 升级失败。

      在新版本中尝试解决,提示 linux image 的 module 未能加载,网上说重装内核可以解决。而 linode 上的 ubuntu 默认使用定制内核,无法 apt reinstall,所以该问题一直无法解决,最后只能将 pptpd 包删除。

    • 升级完成后,要重启系统。
  • 升级完成后,wordpress 站点不能正常访问,还要做一些修改。
    • 删除 apache2 服务 apt remove apache2-bin
    • 删除 apt 备份配置,否则提示扩展名不对。

      mv /etc/apt/apt.conf.d/50unattended-upgrades.ucf-old ~/
      
    • 16.04LTS 上 php 版本升级到 7.0
      • apt install php php-fpm php-mcrypt php-pear php-dev
      • apt-get purge php5-fpm php5-mysql php5-common php5-cli
      • 修改 nginx 中 fpmbackend 为 /run/php/php7.0-fpm.sock
      • mysql_ 系列函数彻底弃用,换成 mysqli_ 系列函数。
      • 修改 fastcgi_params 参数,否则 php 页面总是空白 。

        vim /etc/nginx/fastcgi_params
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO                $fastcgi_script_name;
        
    • 恢复原 nginx.conf 配置,删除 default 站点
    • 恢复 vsftpd.conf 配置
    • 删除并重新安装 phpmyadmin