此demo需要引入okhttp-3.4.1.jar 和 okio-1.9.0.jar(这两个包需要jdk1.7以上的环境)

对应pom文件是:

		<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.4.1</version>
</dependency>

demo文件:HttpClientManage.java 其中的一些日志打印需要替换下

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.FormBody.Builder;
import okhttp3.OkHttpClient;
import okhttp3.Request; import org.apache.commons.lang.StringUtils; import test2.LogUtil; /**
* OkHttp官方文档并不建议我们创建多个OkHttpClient,因此全局使用一个。
* 支持自动读取配置文件中的过期时间配置以及CA导入,或采用默认配置
* 支持实现同步请求(普通+快速),异步请求(普通+快速)
* 支持头文件设置
* 支持https
* 支持代理ip port 若未设置代理ip等信息则采用本机ip常规方式进行请求
* @date 2017年6月10日 下午5:07:14
*/
public class HttpClientManage
{
private static volatile HttpClientManage instance; private static int CONNECT_TIMEOUT = 60;
private static int READ_TIMEOUT = 100;
private static int WRITE_TIMEOUT = 60;
private static int CONNECT_TIMEOUT_QUICK = 3;
private static int READ_TIMEOUT_QUICK = 5;
private static int WRITE_TIMEOUT_QUICK = 3;
/**
* ca证书存放路径
*/
private static String CA_PATH = null;
/**
* 代理域名或者IP
*/
private static String PROXY_IP = null;
/**
* 代理端口
*/
private static int PROXY_PORT = 80;
/**
* 是否忽略正式
*/
private static boolean IS_INORE_CA = true; private static String PARAM_SPLIT_FIRST = "?";
private static String PARAM_SPLIT_VAL = "=";
private static String PARAM_SPLIT = "&";
private static String DEFAULT_ENCODE = "UTF-8";
/**
* ca证书别名
*/
private static String SSL_KEY_ALIAS = "DIY"; /**
* 证书工厂
*/
private static SSLSocketFactory sslSocketFactory = null;
/**
* 证书类型
*/
private static X509TrustManager trustManager = null; /**
* 正常请求
*/
private static OkHttpClient mOkHttpClient; /**
* 局域网内调用的快速请求 响应时间要求比普通正常请求快20倍
*/
private static OkHttpClient mOkHttpClientQuick; /**
* 代理请求
*/
private static OkHttpClient mOkHttpClientProxy; /**
* 单体实例类
* @return
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:00:57
* @since 1.0.0
*/
public static HttpClientManage getInstance()
{
if (instance == null)
{
synchronized (HttpClientManage.class)
{
if (instance == null)
{
instance = new HttpClientManage();
}
}
}
return instance;
} /**
* 简单调用: get(url)
* 加参数调用: get(url, param)
* 加参数同时加头文件调用: get(url, param, header)
* 仅加头文件调用: get(url, null, header)
* @param url 请求地址
* @param params 如果有多个map传入, 则默认第一个为参数param, 第二个为header param
* @return
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:01:50
* @since 1.0.0
*/
public String get(String url, Map<String, String>... params)
{
return getCommon(url, mOkHttpClient, params);
} public String getQuick(String url, Map<String, String>... params)
{
return getCommon(url, mOkHttpClientQuick, params);
} public String getProxy(String url, Map<String, String>... params)
{
if (null != mOkHttpClientProxy)
{
return getCommon(url, mOkHttpClientProxy, params);
}
return get(url, params);
} public String post(String url, Map<String, String>... params)
{
return postCommon(url, mOkHttpClient, params);
} public String postQuick(String url, Map<String, String>... params)
{
return postCommon(url, mOkHttpClientQuick, params);
} public String postProxy(String url, Map<String, String>... params)
{
if (null != mOkHttpClientProxy)
{
return postCommon(url, mOkHttpClientProxy, params);
}
return post(url, params);
} /**
* 异步调用
* 简单调用: get(url, callback)
* 加参数调用: get(url, callbackparam)
* 加参数同时加头文件调用: get(url, callback, param, header)
* 仅加头文件调用: get(url, callback, null, header)
* @param url 请求地址
* @param callback 回调函数
* @param params 如果有多个map传入, 则默认第一个为参数param, 第二个为header param
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:02:59
* @since 1.0.0
*/
public void getAsyn(String url, Callback callback, Map<String, String>... params)
{
getCommonAsyn(url, callback, mOkHttpClient, params);
} public void getQuickAsyn(String url, Callback callback, Map<String, String>... params)
{
getCommonAsyn(url, callback, mOkHttpClientQuick, params);
} public void getProxyAsyn(String url, Callback callback, Map<String, String>... params)
{
if (null != mOkHttpClientProxy)
{
getCommonAsyn(url, callback, mOkHttpClientProxy, params);
return;
}
getAsyn(url, callback, params);
} public void postAsyn(String url, Callback callback, Map<String, String>... params)
{
postCommonAsyn(url, callback, mOkHttpClient, params);
} public void postQuickAsyn(String url, Callback callback, Map<String, String>... params)
{
postCommonAsyn(url, callback, mOkHttpClientQuick, params);
} public void postProxyAsyn(String url, Callback callback, Map<String, String>... params)
{
if (null != mOkHttpClientProxy)
{
postCommonAsyn(url, callback, mOkHttpClientProxy, params);
return;
}
postAsyn(url, callback, params);
} private HttpClientManage()
{
init();
} private void init()
{
// 初始化静态 如果不需要配置文件来进行参数配置,可以将此方法去掉
initProperty();
// 初始化CA
initCa();
initNormalClient();
initQuickClient();
initProxyClient();
} /**
* 初始化参数
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:05:34
* @since 1.0.0
*/
private void initProperty()
{
//IS_INORE_CA = true;
} private void initCa()
{
try
{
if (IS_INORE_CA)
{
trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
} @Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
X509Certificate[] x509Certificates = new X509Certificate[0];
return x509Certificates;
}
};
}
else
{
if (StringUtils.isEmpty(CA_PATH))
{
return;
}
// 读取key
KeyStore keyStore = readKeyStore();
if (null == keyStore)
{
return;
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
{
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];
} SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
}
catch (Exception e)
{
LogUtil.error("initCa error.", e);
}
} private KeyStore readKeyStore()
{
File file = new File(CA_PATH);
if (!file.exists())
{
return null;
} KeyStore keyStore = null;
CertificateFactory certificateFactory = null;
try
{
certificateFactory = CertificateFactory.getInstance("X.509");
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
}
catch (Exception e1)
{
LogUtil.error(null, e1);
} if (null == keyStore || null == certificateFactory)
{
return null;
} InputStream inputStream = null;
if (!file.isDirectory())
{
try
{
inputStream = new FileInputStream(file);
addOneCer(certificateFactory, keyStore, SSL_KEY_ALIAS, inputStream);
}
catch (Exception e)
{
LogUtil.error(null, e);
}
finally
{
try
{
if (inputStream != null)
inputStream.close();
}
catch (IOException e)
{
LogUtil.error(null, e);
}
}
}
else
{
int index = 0;
String certificateAlias;
for (File cerFile : file.listFiles())
{
certificateAlias = SSL_KEY_ALIAS + Integer.toString(index++);
try
{
inputStream = new FileInputStream(cerFile);
addOneCer(certificateFactory, keyStore, certificateAlias, inputStream);
}
catch (Exception e)
{
LogUtil.error(null, e);
}
finally
{
try
{
if (inputStream != null)
inputStream.close();
}
catch (IOException e)
{
LogUtil.error(null, e);
}
}
}
} return keyStore;
} private void addOneCer(CertificateFactory certificateFactory, KeyStore keyStore, String CerAlias,
InputStream certificate) throws KeyStoreException, CertificateException
{
keyStore.setCertificateEntry(CerAlias, certificateFactory.generateCertificate(certificate));
try
{
if (certificate != null)
certificate.close();
}
catch (IOException e)
{
LogUtil.error(null, e);
}
} // 初始化普通请求
private void initNormalClient()
{
// 进行数据初始化
okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder().readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)// 设置读取超时时间
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)// 设置写的超时时间
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);// 设置连接超时时间
// 有证书需要引入
if (null != sslSocketFactory && null != trustManager)
{
builder.sslSocketFactory(sslSocketFactory, trustManager);
}
if (IS_INORE_CA)
{
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
mOkHttpClient = builder.build();
} // 初始化快速请求
private void initQuickClient()
{
okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder()
.readTimeout(READ_TIMEOUT_QUICK, TimeUnit.SECONDS)// 设置读取超时时间
.writeTimeout(WRITE_TIMEOUT_QUICK, TimeUnit.SECONDS)// 设置写的超时时间
.connectTimeout(CONNECT_TIMEOUT_QUICK, TimeUnit.SECONDS);// 设置连接超时时间 // 有证书需要引入
if (null != sslSocketFactory && null != trustManager)
{
builder.sslSocketFactory(sslSocketFactory, trustManager);
}
if (IS_INORE_CA)
{
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
mOkHttpClientQuick = builder.build();
} // 初始化代理请求
private void initProxyClient()
{
if (StringUtils.isEmpty(PROXY_IP))
{
return;
}
okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder().readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)// 设置读取超时时间
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)// 设置写的超时时间
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);// 设置连接超时时间 // 有证书需要引入
if (null != sslSocketFactory && null != trustManager)
{
builder.sslSocketFactory(sslSocketFactory, trustManager);
}
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_IP, PROXY_PORT)));
mOkHttpClientProxy = builder.build();
} private String getCommon(String url, OkHttpClient okHttpClient, Map<String, String>[] params)
{
okhttp3.Request.Builder requestBuilder = createGetBuilder(url, params);
return doSync(url, okHttpClient, requestBuilder, params);
} /**
* 异步请求
* @param url
* @param callback
* @param mOkHttpClient
* @param params
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:05:06
* @since 1.0.0
*/
private void getCommonAsyn(String url, Callback callback, OkHttpClient mOkHttpClient, Map<String, String>[] params)
{
okhttp3.Request.Builder requestBuilder = createGetBuilder(url, params);
doAsyn(url, callback, mOkHttpClient, requestBuilder, params);
} private okhttp3.Request.Builder createGetBuilder(String url, Map<String, String>[] params)
{
String realUrl = url;
Map<String, String> headers = null;
if (null != params)
{
if (params.length > 0)
{
realUrl = buildUrl(url, params[0]);
}
if (params.length > 1)
{
headers = params[1];
}
} okhttp3.Request.Builder requestBuilder = createBuilder(realUrl, headers);
return requestBuilder;
} /**
* 同步请求
* @param url
* @param okHttpClient
* @param requestBuilder
* @param params
* @return
* @exception/throws [异常类型] [异常说明](可选)
* @date 2017年6月10日 下午5:04:29
* @since 1.0.0
*/
private String doSync(String url, OkHttpClient okHttpClient, okhttp3.Request.Builder requestBuilder,
Map<String, String>[] params)
{
try
{
printRequestInfo(url, params); String response = okHttpClient.newCall(requestBuilder.build()).execute().body().string(); printResponseInfo(response);
return response;
}
catch (IOException e)
{
LogUtil.error("url:" + url + ",params:" + JsonTool.toJson(params), e);
}
return null;
} private void printRequestInfo(String url, Map<String, String>[] params)
{
LogUtil.debug("url:" + url + "\n params:" + JsonTool.toJson(params).replace("\\", ""));
} private void printResponseInfo(String response)
{
LogUtil.debug("response:" + response);
} /**
* 异步请求
* @param url
* @param callback
* @param okHttpClient
* @param requestBuilder
* @param params
* @exception/throws [异常类型] [异常说明](可选)
* @since 1.0.0
*/
private void doAsyn(String url, Callback callback, OkHttpClient okHttpClient,
okhttp3.Request.Builder requestBuilder, Map<String, String>[] params)
{
printRequestInfo(url, params);
okHttpClient.newCall(requestBuilder.build()).enqueue(callback);
// printResponseInfo(response);
} private okhttp3.Request.Builder createBuilder(String url, Map<String, String> headers)
{
okhttp3.Request.Builder builder = new Request.Builder().url(url);
if (null == headers)
{
return builder;
} for (Map.Entry<String, String> en : headers.entrySet())
{
builder.addHeader(en.getKey(), en.getValue());
}
return builder;
} private String buildUrl(String url, Map<String, String> param)
{
if (null == param)
{
return url;
}
StringBuilder sb = new StringBuilder();
try
{
for (Map.Entry<String, String> en : param.entrySet())
{
if (!StringUtils.isEmpty(en.getKey()) && !StringUtils.isEmpty(en.getValue()))
{
sb.append(PARAM_SPLIT);
sb.append(URLEncoder.encode(en.getKey(), DEFAULT_ENCODE));
sb.append(PARAM_SPLIT_VAL);
sb.append(URLEncoder.encode(en.getValue(), DEFAULT_ENCODE));
}
}
}
catch (UnsupportedEncodingException e)
{
LogUtil.error("url=" + url, e);
}
if (sb.length() > 0)
{
if (!url.contains(PARAM_SPLIT_FIRST))
{
url = url + PARAM_SPLIT_FIRST;
}
url = url + sb.substring(1);
}
return url;
} private String postCommon(String url, OkHttpClient okHttpClient, Map<String, String>[] params)
{
okhttp3.Request.Builder requestBuilder = createPostBuilder(url, params);
return doSync(url, okHttpClient, requestBuilder, params);
} private void postCommonAsyn(String url, Callback callback, OkHttpClient okHttpClient, Map<String, String>[] params)
{
okhttp3.Request.Builder requestBuilder = createPostBuilder(url, params);
doAsyn(url, callback, okHttpClient, requestBuilder, params);
} private okhttp3.Request.Builder createPostBuilder(String url, Map<String, String>[] params)
{
Map<String, String> headers = null;
Builder formBuilder = new FormBody.Builder();
if (null != params)
{
if (params.length > 0 && null != params[0])
{
for (Map.Entry<String, String> en : params[0].entrySet())
{
formBuilder.add(en.getKey(), en.getValue());
}
} if (params.length > 1)
{
headers = params[1];
}
}
okhttp3.Request.Builder requestBuilder = createBuilder(url, headers).post(formBuilder.build());
return requestBuilder;
} /**
* 文件上传
* @param urlStore 上传服务地址
* @param file 本地文件
* @param callback 回调函数,若无则为同步,同步则直接返回result,异步则在callback中返回上传结果
* @param params 第一个map为body中需要传的参数,第二个map为头文件中需要传的参数
* @return
* @exception/throws [异常类型] [异常说明](可选)
* @date 2018年5月11日 上午8:47:24
*/
public String upload(String urlStore, File file, Callback callback, Map<String, String>... params)
{
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream") , file);
okhttp3.MultipartBody.Builder mBodyBuilder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("desc" , file.getName())
.addFormDataPart("file" , file.getName(), fileBody);
Map<String, String> paramMap = null;
if (null != params && params.length > 0)
{
paramMap = params[1];
} ;
if (null != paramMap)
{
for (Map.Entry<String, String> en : paramMap.entrySet())
{
mBodyBuilder.addFormDataPart(en.getKey(), en.getValue());
}
}
Map<String, String> headMap = null;
if (null != params && params.length > 1)
{
headMap = params[1];
} okhttp3.Request.Builder requestBuilder = createBuilder(urlStore, headMap);
requestBuilder.post(mBodyBuilder.build()); if (null == callback)
{
//同步
return doSync(urlStore, mOkHttpClient, requestBuilder, params);
}
else
{
//异步
doAsyn(urlStore, callback, mOkHttpClient, requestBuilder, params);
return null;
}
} }

是否https证书验证(默认不做证书校验,建议生成环境一定要做证书校验,哪怕是导入私有证书),配置

IS_INORE_CA = false

导入私有证书,配置

CA_PATH 配置CA证书(就是浏览器证书导出的那个证书)路径即可

设置代理服务器,配置

PROXY_IP和PROXY_PORT即可(默认不采用代理)

上传文件

String rs = HttpClientManage.getInstance().upload(urlStore, file, null, null);

其中rs为同步返回的结果。

OKHttp官网不建议使用多个实例,因为每个实例都自己配置了线程管理,所以项目中不涉及基础信息的改动(比如,超时时间,证书等),建议就共用一个实例。

OKHttp使用demo(证书过滤,证书导入,代理访问,文件上传)的更多相关文章

  1. 【Demo Project】AjaxSubmit+Servlet表单文件上传和下载

    一.背景 前段时间公司要求我做一个上传和下载固件的页面,以备硬件产品在线升级,现在我把这部分功能抽取出来作为一个Demo Project给大家分享. 话不多说,先看项目演示 --> 演示  源码 ...

  2. 文件上传下下载(不包含断点续传) Excel,Word导入导出基础

    1.文件上传下载(MVC应用) 视图:form表单,编码方式为multipart/form-data <body> <div> <form action="/D ...

  3. requests 进阶用法学习(文件上传、cookies设置、代理设置)

    一.文件上传 1.模拟网站提交文件 提交此图片,图片名称:timg.jpg import requests files={ 'file':open('timg.jpg','rb') } respons ...

  4. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  5. SpringMVC文件上传 Excle文件 Poi解析 验证 去重 并批量导入 MYSQL数据库

    SpringMVC文件上传 Excle文件 Poi解析并批量导入 MYSQL数据库  /** * 业务需求说明: * 1 批量导入成员 并且 自主创建账号 * 2 校验数据格式 且 重复导入提示 已被 ...

  6. 结合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程

    1.bootstrap-fileinpu的简单介绍 在前面的随笔,我介绍了Bootstrap-table表格插件的具体项目应用过程,本篇随笔介绍另外一个Bootstrap FieInput插件的使用, ...

  7. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  8. struts2多文件上传(带进度条)demo+说明

    利用plupload插件实现多文件上传,实现图片: 在jsp写入js代码: z<%@ page language="java" contentType="text/ ...

  9. WebSite 文件上传Demo

    知识点: 1 <!--上传文件时:        1.必须使用Post方式来提交数据        2.必须设置表单的enctype属性        3.必须在表单中包含文件域.input t ...

随机推荐

  1. CVPR2018_Crafting a Toolchain for Image Restoration by Deep Reinforcement Learning

    CVPR2018_Crafting a Toolchain for Image Restoration by Deep Reinforcement Learning http://mmlab.ie.c ...

  2. Centos7在线安装MySQL

    wget dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpmyum localinstall mysql57-community- ...

  3. ES6读书笔记(三)

    前言 前段时间整理了ES6的读书笔记:<ES6读书笔记(一)>,<ES6读书笔记(二)>,现在为第三篇,本篇内容包括: 一.Promise 二.Iterator和for of循 ...

  4. DDL-库的管理

    一.创建库create database [if not exists] 库名[ character set 字符集名]; 二.修改库alter database 库名 character set 字 ...

  5. 使用属性Props完成一张卡片

    一:我们先安装bootstrap,为了使我们的样式好看些 cnpm  install bootstrap  --save 二:我们在index.js中引入bootstap Import ‘bootst ...

  6. 不可变字符串String与可变字符串StringBuilder、StringBuffer使用详解

    String字符串 char类型只能表示一个字符,而String可以表示字符串,也就是一个字符序列.但String不是基本类型,而是一个定义好的类,是一个引用类型.在Java中,可以将字符串直接量赋给 ...

  7. mysql5.7 安装版 表不能输入汉字解决方案

    安装版本 的安装目录没有 my.ini 配置文件 在所在表执行 alter table 数据表名 CONVERT TO CHARACTER SET utf8;

  8. mac 装5.6版本mysql 设置密码

    最的mysql在装的时候就可以设置 ,但是低版本的好像不行,需要在装了以后才能设置. mac下,mysql5.7.18连接出错,错误信息为:Access denied for user 'root'@ ...

  9. shell习题第8题:监控nginx的502状态

    [题目要求] 服务器上跑的是LNMP环境,近期总是有502现象.502为网站访问的状态码,200正常,502错误是nginx最为普遍的错误状态码. 由于502只是暂时的,并且只要一重启php-fpm服 ...

  10. c# 在WebBrowser中用SendMessage模拟鼠标点击

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...