Total Pageviews

2016/08/08

[API] [JAVA] How to send SMS via 三竹簡訊

Requirement
Our web application has SMS (short message service) requirement. Our customer asks to send short message by 三竹簡訊.

Based on its documentation, it provide 8 ways to send short message, and we choose approach 4th.


Here has an diagram to describe 三竹資訊's architecture:


We will use Http Get method to send user name, password, phone number and short message to SmGateway:


And it will return response to your source code:


The status code and its description are as follows:



How-to
The process looks like:


Here has the sample code.
This enumeration contains the status code and its description:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package albert.practice.sms;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
public enum SmsStatusCodeEnum {

    STATUS_STAR("*", "系統發生錯誤,請聯絡三竹資訊窗口人員"),
    STATUS_a("a", "簡訊發送功能暫時停止服務,請稍候再試"),
    STATUS_b("b", "簡訊發送功能暫時停止服務,請稍候再試"),
    STATUS_c("c", "請輸入帳號"),
    STATUS_d("d", "請輸入密碼"),
    STATUS_e("e", "帳號、密碼錯誤"),
    STATUS_f("f", "帳號已過期"),
    STATUS_h("h", "帳號已被停用"),
    STATUS_k("k", "無效的連線位址"),
    STATUS_m("m", "必須變更密碼,在變更密碼前,無法使用簡訊發送服務"),
    STATUS_n("n", "密碼已逾期,在變更密碼前,將無法使用簡訊發送服務"),
    STATUS_p("p", "沒有權限使用外部Http程式"),
    STATUS_r("r", "系統暫停服務,請稍後再試"),
    STATUS_s("s", "帳務處理失敗,無法發送簡訊"),
    STATUS_t("t", "簡訊已過期"),
    STATUS_u("u", "簡訊內容不得為空白"),
    STATUS_v("v", "無效的手機號碼"),
    STATUS_0("0", "預約傳送中"),
    STATUS_1("1", "已送達業者"),
    STATUS_2("2", "已送達業者"),
    STATUS_3("3", "已送達業者"),
    STATUS_4("4", "已送達手機"),
    STATUS_5("5", "內容有錯誤"),
    STATUS_6("6", "門號有錯誤"),
    STATUS_7("7", "簡訊已停用"),
    STATUS_8("8", "逾時無送達"),
    STATUS_9("9", "預約已取消");

    @Getter
    private String code;

    @Getter
    private String description;

}

This utility class is used to send short message:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package albert.practice.sms;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.StringUtils;

import com.google.common.collect.Lists;

/**
 * username = 使用者帳號。SmGateway資料庫表格SMUser中需有此使用者,且狀態為啟用。 <br>
 * password = 使用者密碼 <br>
 * dstaddr = 受訊方手機號碼 <br>
 * DestName = 收訊人名稱。若其他系統需要與簡訊資料進行系統整合,此欄位可填入來源系統所產生的Key值,以對應回來源資料庫 <br>
 * dlvtime = 簡訊預約時間。格式為YYYY-MM-DD HH:NN:SS或YYYYMMDDHHNNSS,或是整數值代表幾秒後傳送。<br>
 * vldtime = 簡訊有效期限。格式為YYYY-MM-DD HH:NN:SS或YYYYMMDDHHNNSS,或是整數值代表傳送後幾秒後內有效。 <br>
 * smbody = 簡訊內容。必須為BIG-5編碼,長度70個中文字或是160個英數字。若有換行的需求,請填入ASCII Code 6代表換行。 <br>
 */
@Slf4j
public class SmsUtils {

    private final static String USER_AGENT = "Mozilla/5.0";

    private final String SMS_URL = "http://xxx/SmSendGet.asp?";
    private final String USER_NAME = "Test001";
    private final String PASSWORD = "TestPwd";

    public static void main(String[] args) throws Exception {
        String phone = "09";
        String smBody = "您的驗證碼為○○○○○○,請於收到簡訊10分鐘內完成驗證。";
        new SmsUtils().sendSms(phone, smBody);
    }

    public void sendSms(String phoneNumber, String body) throws Exception {
        body = URLEncoder.encode(body, "Big5");
        sendGet(phoneNumber, body);
    }

    private void sendGet(String phoneNumber, String body) throws IOException {

        StringBuilder url = new StringBuilder();
        url.append(SMS_URL);
        url.append("username=").append(USER_NAME);
        url.append("&password=").append(PASSWORD);
        url.append("&dstaddr=").append(phoneNumber);
        url.append("&smbody=").append(body);

        URL obj = null;
        BufferedReader bufferedReader = null;
        try {
            obj = new URL(url.toString());
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();

            // optional default is GET
            con.setRequestMethod("GET");

            // add request header
            con.setRequestProperty("User-Agent", USER_AGENT);

            int responseCode = con.getResponseCode();

            log.debug("\nSending 'GET' request to URL : " + url);
            log.debug("Response Code : " + responseCode);

            bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine = "";
            StringBuffer response = new StringBuffer();

            while ((inputLine = bufferedReader.readLine()) != null) {
                response.append(inputLine);
            }

            // print result
            String responseStr = response.toString();
            log.debug(responseStr);

            int from = responseStr.indexOf("statuscode=");
            int to = responseStr.indexOf("AccountPoint=");
            String tmpStr = StringUtils.substring(responseStr, from, to);

            // 取得status code
            String statusCode = StringUtils.substring(tmpStr, tmpStr.length() - 1, tmpStr.length());
            log.info("statusCode = " + statusCode);

            // 將status code 轉成 中文訊息
            String executionResult = getDescription(statusCode);
            log.info("executionResult = " + executionResult);

            List<String> successList = Lists.newArrayList("0", "1", "2", "3", "4"); // 成功的SATUSCODE
            if (!successList.contains(statusCode)) {
                throw new RuntimeException(executionResult);
            }

        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }
    }

    /**
     * 將status code 轉成 中文訊息
     * 
     * @param statusCode
     * @return 中文訊息
     */
    private String getDescription(String statusCode) {
        String description = "";
        for (SmsStatusCodeEnum smsStatusCode : SmsStatusCodeEnum.values()) {
            if (smsStatusCode.getCode().equals(statusCode)) {
                description = smsStatusCode.getDescription();
            }
        }
        return description;
    }
}









2016/08/07

[Visual Studio Code] How to Change Visual Studio Code Display Language

Problem
After I installed Visual Studio Code, its display language show Transitional Chinese. How do I change it to English?


How-To
Step1. Press F1


Step2. type config in Command Palette and choose Configure Language


Step3. Update locale from zh-tw to en-US




Step4. restart Visual Studio Code



Reference
[1] https://code.visualstudio.com/docs/customization/locales

2016/08/06

[Websphere] WTRN0006W: Transaction has timed out after 120 seconds

Problem
I am deploying WAR in WAS 7. 
I got this error message as I do test:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[2016-05-16 15:28:23][INFO][Non-deferrable Alarm : 1][Line:1709][com.ibm.ws.tx.jta.TimeoutManager.logToJSR47Logger]WTRN0006W: Transaction 00000154B8756C5F00000003618D9380FB564FCFE7EE45F38E06607A9BFB0CE57778367B00000154B8756C5F00000003618D9380FB564FCFE7EE45F38E06607A9BFB0CE57778367B00000001 has timed out after 120 seconds.
[2016-05-16 15:28:23][INFO][Non-deferrable Alarm : 1][Line:1709][com.ibm.ws.tx.jta.TimeoutManager.logToJSR47Logger]WTRN0124I: When the timeout occurred the thread with which the transaction is, or was most recently, associated was Thread[SimpleAsyncTaskExecutor-1,5,main]. The stack trace of this thread when the timeout occurred was: 
 java.io.RandomAccessFile.readBytes(Native Method)
 java.io.RandomAccessFile.read(RandomAccessFile.java:368)
 org.apache.fontbox.ttf.BufferedRandomAccessFile.fillBuffer(BufferedRandomAccessFile.java:122)
 org.apache.fontbox.ttf.BufferedRandomAccessFile.read(BufferedRandomAccessFile.java:160)
 org.apache.fontbox.ttf.RAFDataStream.read(RAFDataStream.java:162)
 org.apache.fontbox.ttf.TTFDataStream.read(TTFDataStream.java:263)
 org.apache.fontbox.ttf.TTFDataStream.readString(TTFDataStream.java:91)
 org.apache.fontbox.ttf.TTFDataStream.readString(TTFDataStream.java:64)
 org.apache.fontbox.ttf.TTFParser.readTableDirectory(TTFParser.java:232)
 org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:139)
 org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:87)
 org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.addTrueTypeFont(FileSystemFontProvider.java:502)
 org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.scanFonts(FileSystemFontProvider.java:246)
 org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.<init>(FileSystemFontProvider.java:225)
 org.apache.pdfbox.pdmodel.font.FontMapperImpl$DefaultFontProvider.<clinit>(FontMapperImpl.java:132)
 java.lang.J9VMInternals.initializeImpl(Native Method)
 java.lang.J9VMInternals.initialize(J9VMInternals.java:236)
 org.apache.pdfbox.pdmodel.font.FontMapperImpl.getProvider(FontMapperImpl.java:151)
 org.apache.pdfbox.pdmodel.font.FontMapperImpl.findFont(FontMapperImpl.java:415)
 org.apache.pdfbox.pdmodel.font.FontMapperImpl.findFontBoxFont(FontMapperImpl.java:378)
 org.apache.pdfbox.pdmodel.font.FontMapperImpl.getFontBoxFont(FontMapperImpl.java:352)
 org.apache.pdfbox.pdmodel.font.PDType1Font.<init>(PDType1Font.java:115)
 org.apache.pdfbox.pdmodel.font.PDType1Font.<clinit>(PDType1Font.java:76)
 java.lang.J9VMInternals.initializeImpl(Native Method)
 java.lang.J9VMInternals.initialize(J9VMInternals.java:236)
 org.apache.pdfbox.pdmodel.font.PDFontFactory.createFont(PDFontFactory.java:62)
 org.apache.pdfbox.pdmodel.PDResources.getFont(PDResources.java:123)
 org.apache.pdfbox.contentstream.operator.text.SetFontAndSize.process(SetFontAndSize.java:60)
 org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:815)
 org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:472)
 org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:446)
 org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:149)
 org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:189)
 org.apache.pdfbox.rendering.PDFRenderer.renderPage(PDFRenderer.java:208)
 org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:139)
 org.apache.pdfbox.rendering.PDFRenderer.renderImageWithDPI(PDFRenderer.java:94)


How To
Here has a workaround approach, but you need to figure out the reason why your program had spent just a long time to execute. If your program really need more time to execute, you should log into WAS administration console to modify the value of Total transaction lifetime timeout and Maximum transaction timeout.

The configuration steps are as follows:

  • Step1. Log in to administration console
  • Step2. WebSphere Application Server 7.0: Click Servers > Server Types > WebSphere application servers > server_name > Container Services > Transaction Service.
  • Step3. Increase the Total transaction lifetime timeout. The default value is 120.
  • Step4. Increase the Maximum transaction timeout. The default value is 300 seconds.



  • Step5. Save your changes and restart WAS


Reference
[1] https://stackoverflow.com/questions/23004900/how-to-resolve-this-timeout-exception-in-was-7

2016/08/05

[Java Mail] Encoding problem

Problem
I am using Java Mail API to sent test mail.
Assume I write email content with Traditional Chinese, all characters are broken.


How-To
We need to set default encoding to 'UTF-8' before send.
Here has the sample code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.io.File;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

public class MailTest {

    public static void main(String[] args) {

        Properties props = new Properties();
        props.put("mail.smtp.starttls.enable", "true");

        String host = "smtp.gmail.com";
        int port = 587;
        String userName = "xxx";
        String password = "xxx";

        String mailTo = "xxx@gmail.com";
        String subject = "Hello my friend~";

        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        sender.setJavaMailProperties(props);
        sender.setHost(host);
        sender.setPort(port);
        sender.setUsername(userName);
        sender.setPassword(password);

        // set email default encoding
        sender.setDefaultEncoding("UTF-8");

        String content = "<p>親愛的客戶您好:</p>";
        content += "<p>您的網路申請驗證碼為<font color='red'>12345</font>,僅限會員申請使用,請於收到e-mail <font color='red'>10分鐘內完成驗證,10分鐘後將自動失效</font></p>";
        content += "<p>XXXX敬上</p>";

        MimeMessage message = sender.createMimeMessage();
        try {

            MimeMessageHelper helper;
            helper = new MimeMessageHelper(message, true);
            helper.setTo(mailTo);
            helper.setSubject(subject);

            // true means it is a html format email
            helper.setText(content, true);

     // attachment1
            FileSystemResource panda01 = new FileSystemResource(new File(
                    "D://dropbox//picture//201602011033331.png"));
            helper.addInline("pand01", panda01);

     // attachment2
            FileSystemResource panda02 = new FileSystemResource(new File(
                    "D://dropbox//picture//panda.png"));
            helper.addInline("panda02", panda02);

        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }

 //sent mail
        sender.send(message);
    }
}

Check the mail:

Reference
[1] http://stackoverflow.com/questions/5636255/unicode-chars-and-spring-javamailsenderimpl-no-unicode-chars-under-linux

2016/08/04

[JavaMail] Attachment name does not show correctly in Email

Problem
I am using JavaMail API to send email. But the attachment name does not show correctly.
The name should be 退匯明細表.pdf‎, but I got ATT97145.pdf‎.

The incorrect email looks like:


The expected email looks like:


Here is the code snippet to add attachment in email:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
private void attachFiles(MimeMessageHelper messageHelper, List<File> attachFiles)
        throws MessagingException {
    for (File attachFile : attachFiles) {
        FileSystemResource file = new FileSystemResource(attachFile);
        log.info("attachFile.getName():" + attachFile.getName());

        try {
            messageHelper.addAttachment(attachFile.getName(), file);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}


Solution
You can utilize MimeUtility.encodeText to handle the file name.
Here is the updated code snippet:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private void attachFiles(MimeMessageHelper messageHelper, List<File> attachFiles)
        throws MessagingException {
    for (File attachFile : attachFiles) {
        FileSystemResource file = new FileSystemResource(attachFile);
        log.info("attachFile.getName():" + attachFile.getName());

        try {
            messageHelper.addAttachment(
                    MimeUtility.encodeText(attachFile.getName(), "UTF-8", null), file);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}


Reference
[1] http://docs.oracle.com/javaee/6/api/javax/mail/internet/MimeUtility.html#encodeText(java.lang.String, java.lang.String, java.lang.String)

2016/08/03

[Java Mail] How to check Exchange Server status?

Problem
If I would like to write a Java code to check the status of SMTP server, how to do it?

How-to
The sample code is as bellows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package albert.practice.mail;

import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;

import lombok.extern.slf4j.Slf4j;

import org.springframework.mail.javamail.JavaMailSenderImpl;

/**
 * Used to check the status of exchange server .
 */
@Slf4j
public class CheckSmtpStatus {

    /**
     * The main method.
     * 
     * @param args
     *            the arguments
     * @throws MessagingException
     *             the messaging exception
     */
    public static void main(String[] args) throws MessagingException {
        String host = "smtp host name";
        int port = 25;
        String emailU = "username";
        String emailP = "password";

        CheckSmtpStatus checkSmtpStatus = new CheckSmtpStatus();
        boolean isConnected = checkSmtpStatus.isConnected(host, port, emailU, emailP);
        log.debug("isConnected = " + isConnected);
    }

    /**
     * Checks if is connected.
     * 
     * @param host
     *            the host
     * @param port
     *            the port
     * @param emailU
     *            the email u
     * @param emailP
     *            the email p
     * @return true, if is connected
     * @throws MessagingException
     *             the messaging exception
     */
    public boolean isConnected(String host, int port, String emailU, String emailP)
            throws MessagingException {
        boolean isConnected = false;
        Transport transport = null;
        Session session = createJavaMailSender(host, port, emailU, emailP);
        try {
            transport = session.getTransport("smtp");
            transport.connect(host, port, emailU, emailP);

            // used to check this SMTP service is currently connected or not
            isConnected = transport.isConnected();
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        } finally {
            transport.close();
        }
        return isConnected;
    }

    private Session createJavaMailSender(String host, int port, String emailU, String emailP) {
        Properties properties = new Properties();
        properties.setProperty("mail.debug", "true");
        properties.setProperty("mail.smtp.auth", "true");
        properties.put("mail.smtp.ssl.trust", "*");

        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
        javaMailSender.setJavaMailProperties(properties);
        javaMailSender.setHost(host);
        javaMailSender.setPort(port);
        javaMailSender.setDefaultEncoding("UTF-8");
        javaMailSender.setUsername(emailU);
        javaMailSender.setPassword(emailP);

        return javaMailSender.getSession();
    }

}




2016/08/02

[閱讀筆記] ZERO to ONE (Part 2)


  1. 經營事業就像下棋一樣。西洋棋王卡布卡蘭卡曾說:「要成功,首要工作就是必須先研究殘局」
  2. 膚淺的人相信運氣,相信環境;意志堅定的人相信因果
  3. 勝利是留給凡事都準備好的人,但一般人都以為只是運氣好
  4. 反摩爾定律觀察到,從1950年開始,每花十億元在新藥研發得到核准通過的新藥數量每隔九年就會減少一半,這跟摩爾定律恰好相反。由於同時間資訊科技業比過去有更快速的發展,因此今天的生技公司面對的大問題是未來能否看到有類似的發展。
  5. 創投的最大秘密是,成功基金的最佳投資應該等於或超過基金其他投資的總和
  6. 如果你是創投者,當你把專注焦點從事業的本質,移轉到是否合乎多元化避險策略的財務問題,那創投者(也就是你)看起來就會很像在買彩券。一旦你以為自己在抽獎,你就要做好輸錢的心理準備。
  7. 冪次法則 (Power Law) = 長尾理論,如80%的人只願意買當中20%的商品,而其餘80%的商品則只有20%的人想購買;又如這個產業80%的獲利被市占率前幾名的公司吃掉,其他公司只能吃剩下的20%的利潤
  8. 你要想清楚,你擅長的事,在未來是否有前景
  9. 在冪次法則 (Power Law) 的世界,你必須非常努力思考自己的行動到底會落在80/20曲線的哪一段,代價太大了。如果你真的自己開公司,一定要知道冪次法則 (Power Law) ,好好的經營。最重要的事情只有一件:一個市場會比其他市場更有利。時間和決策也有冪次法則 (Power Law) ,某些鍵時刻的決策的重要性,將遠比其他時刻更重要。
  10. 獨佔事業的老闆對他們的壟斷地位總是輕描淡寫,避免政府監管;競爭激烈的企業卻策略性的誇大他們的獨特之處
  11. 每個偉大企業都建構在不為外界所知的秘密上,一間偉大公司就是一個合力改變世界的計畫
  12. 身為創業者,你的首要工作是把第一件事情做對,因為你無法在有缺陷的基礎上,建立偉大的公司。早期做了一個差勁的決定就很難在日後彌補,例如你選錯創業夥伴或是雇用不對的人。記住,基礎決定命運
  13. 沒有團隊的支援,想要從0到1非常困難
  14. 一個人數很多的股東會,根本無法有效監督公司的經營,這只是為幕後的獨裁者提供掩飾。如果你不想受到董事會約束,就把股東會的規模膨脹到很大。如果要有一個有成效的董事會,規模必須很小,基本上不會超過五個,除非是上市公司,政府規定九個
  15. 一家公司付給CEO的錢越少,表現就會越好,這是我投資數百家新創事業所注意到的事情。高薪會給CEO誘因維持現狀,像個政客,捍衛目前的薪水,而不是和其他人一起努力找出問題並積極解決。缺少現金的CEO反而會專注提高公司的整體價值。
  16. 績效獎金是鼓勵獲得短期價值的思維。任何用現金支付的薪資,都是比較著眼在現在,比較沒有著眼在未來
  17. 股票是一種報酬,可以有效引導員工去創造未來的價值
  18. 股票無法創造完美的誘因吸引員工加入公司,卻是創辦人能讓大家都站在同一陣線的最好方法
  19. 內部和諧是新創公司的存活條件。我們常以為失敗的新創公司是在競爭激烈的ecosystem 中敗給嗜血的對手,但每家公司都有自己的 ecosystem,派系惡鬥會讓公司無力應付外在的威脅。
  20. 廣告的目的並不是要你馬上買產品,而是要讓你不知不覺地將產品的印象深植腦中,之後再帶動銷售。一個無法察覺廣告影響的人,反而容易被騙
  21. 要把銷售想成產品設計不可或缺的部分,如果發明新東西卻沒有找到有效的銷售方法,不管產品多棒,生意都不會好
  22. 未來最有價值的企業會問的是:電腦如何協助人類解決困難的問題
  23. 優秀的專業計畫必須回答這七個問題:(1)工程問題:你創造的是突破的還是微幅改善的科技;(2)時機問題:現在是開始這個事業的正確時機嗎;(3)獨佔問題:你一開始就在小型市場搶到高市佔率嗎;(4)人員問題:你有適合的團隊嗎;(5)銷售問題:你除了有開發產品的能力,你還有銷售能力嗎;(6)持久問題:你的市場定位可以堅持10年、20年嗎;(7)秘密問題:你是否已經找到別人沒看見的獨特商機
  24. 創業家一開始的挑戰是,先找到一個小市場,並搶下市佔率
  25. 創業者之所以重要,並非因為只有他們的工作才有價值,而是優秀的創業者可以激發其下員工繳出最佳表現
  26. 要從 0 到 1,最重要的第一步是自己獨立思考