示例#1
0
func main() {
	//set runtime
	runtime.GOMAXPROCS(runtime.NumCPU())

	log.Log("", "Welcom to use Caesar. Caesar is a high performance message queue.", nil)
	loadConf()
	sflag.FlagResolve(&listenAddr)

	//resolve TCPAddress
	log.Log("info", "Now start server...", log.Fields{"Listen Address": listenAddr})
	tcpAddr, err := net.ResolveTCPAddr("tcp4", listenAddr)
	handleErr(err)

	//Create listener
	ln, err := net.ListenTCP("tcp4", tcpAddr)

	//start message server service
	go control.MsgServerStart()

	//start control server service
	control.Init(ln)
	//	rpcServer := rpc.NewServer()
	//	users := new(command.Users)
	//	rpcServer.Register(users)
	//	log.Log("info", "Server start success, and now accept request from client.", nil)
	//	rpcServer.Accept(ln)
}
示例#2
0
文件: db.go 项目: jmptrader/Caesar
func CreateUsertoDB(userTable *User_Table) error {
	err1 := DB.Connect()
	defer DB.Close()
	if err1 != nil {
		log.Log("err", err1.Error(), nil)
		return err1
	}
	stmt, err := DB.Prepare("insert into user values (?, ?, ?, ?, ?, ?,?,?)")
	handleErr(err)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	stmt.Bind(userTable)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//err = getData(msgq)
	stmt.Bind(userTable.Id, userTable.Group_id, userTable.Name, userTable.Password, userTable.Register_time, userTable.Sign, userTable.Last_login_time, userTable.Other)
	log.Log("info", string(userTable.Id), nil)
	_, err = stmt.Run() //msgq.MQid, msgq.MQname, msgq.MQType, msgq.Owner, msgq.Persistence
	handleErr(err)
	return nil
}
示例#3
0
文件: db.go 项目: jmptrader/Caesar
func DeleteMqById(mqid int) error {
	_, err := GetMqDataById(mqid)
	if err != nil {
		return err
	}

	err1 := DB.Connect()
	defer DB.Close()
	if err1 != nil {
		log.Log("err", err1.Error(), nil)
		return err1
	}
	stmt, err := DB.Prepare(`delete from msgqueue where msgqueue_id=?`)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//stmt.Bind(mqid)
	//_, _, err = stmt.Exec(mqid)
	_, _, err = stmt.Exec(mqid)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//	num := res.AffectedRows()
	//	fmt.Println(num)
	return nil
}
示例#4
0
func init() {
	err := config.ReadConfigFile("../config/msgserver.yaml")
	if err != nil {
		//fmt.Print(err)
		mylog.Log("err", err.Error(), nil)
	} else {
		mylog.Log("info", "Message server config read!", nil)
	}
	ListenPort, err = config.GetString("listenport")
	handleErr(err)
}
示例#5
0
//Load configuration file.
func loadConf() {
	log.Log("info", "Load config file...", nil)
	err := config.ReadConfigFile("../config/server.yaml")
	if err != nil {
		//fmt.Print(err)
		log.Log("err", err.Error(), nil)
	} else {
		log.Log("info", "Completely read!", nil)
	}

	listenAddr, err = config.GetString("rpcAddress")
	handleErr(err)
	msgAddr, err = config.GetString("msgAddress")
	handleErr(err)
}
示例#6
0
文件: mqMsg.go 项目: jmptrader/Caesar
func (mqmsg MqMsg) PostMsg(w rest.ResponseWriter, r *rest.Request) {
	//msg := message.NewMsg()
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := DefaultMM[mqid]
	if ok {
		mqmsg.Msg.MQid = mqid
		mqmsg.Msg.Generator = mq.Owner
		mqmsg.Msg.MsgId = Counter
		Counter++
		r.DecodeJsonPayload(mqmsg.Msg)

		fmt.Println(mqmsg.Msg.Value)

		//fmt.Println("post msg")
		mq.Lock()
		mq.AddMsg(*mqmsg.Msg)
		mq.Unlock()
		//w.WriteJson(mqmsg.Msg)
		w.WriteJson(map[string]string{"1016": "post success"})
	} else {
		w.WriteJson(map[string]string{"1010": "mq not running"})
	}
}
示例#7
0
func checkLogined() bool {
	if StatusLine == 0 {
		log.Log("info", "You are offline, please login.", nil)
		return true
	} else {
		return false
	}
}
示例#8
0
文件: db.go 项目: jmptrader/Caesar
func init() {
	err := config.ReadConfigFile("../config/db.yaml")
	if err != nil {
		//fmt.Print(err)
		log.Log("err", err.Error(), nil)
	} else {
		log.Log("info", "DB config read!", nil)
	}
	user, err = config.GetString("username")
	handleErr(err)
	pwd, err = config.GetString("password")
	handleErr(err)
	dbname, err = config.GetString("dbname")
	handleErr(err)
	dbaddress, err = config.GetString("address")
	handleErr(err)
	//log.Log("info", "", log.Fields{"username":user,"password":pwd,"dbname":dbname,"address":dbaddress})
	DB = mysql.New("tcp", "", dbaddress, user, pwd, dbname)
}
示例#9
0
文件: db.go 项目: jmptrader/Caesar
func DeleteMqByUserId(userId int) error {
	err := DB.Connect()
	defer DB.Close()
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	stmt, err := DB.Prepare(`delete from msgqueue where user_id=?`)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	_, _, err = stmt.Exec(userId)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	return nil
}
示例#10
0
//init the defaultServer, publice receiver's method and accept the listener from client.
func Init(ln *net.TCPListener) {
	//DefaultServer = rpc.NewServer()
	//test := new(Test)
	//DefaultServer.Register(test)
	//users := new(command.Users)
	//rpcServer.Register(users)
	Register()
	log.Log("info", "Server start success, and now accept connection request from client.", nil)
	DefaultServer.Accept(ln)
	//log.Log("info", "run here now.", nil)
}
示例#11
0
文件: user.go 项目: jmptrader/Caesar
func (users *Users) LogOff(user *User, res *string) error {
	if users.isLogined(user) && user.Key == users.UM[user.Name].Key {
		*res = ""
		delete(users.UM, user.Name)
		//fmt.Printf("%s exit.\n", user.Name)
		logs := "Client " + user.Name + " logoff."
		log.Log("info", logs, nil)
	} else {
		*res = ""
	}
	return nil
}
示例#12
0
文件: table.go 项目: jmptrader/Caesar
func (lead *User_Table) CreateUser(userTable *User_Table, res *string) error {
	//fmt.Println("here")
	//log.Log("info", mqtable.Name, nil)
	err := CreateUsertoDB(userTable)
	if err != nil {
		*res = "User create failed!"
	} else {
		*res = userTable.Name + " create success."
		log.Log("info", userTable.Name+" created.", nil)
	}
	return nil
}
示例#13
0
文件: table.go 项目: jmptrader/Caesar
func (lead *Mq_Table) CreateMQ(mqtable *Mq_Table, res *string) error {
	//fmt.Println("here")
	//log.Log("info", mqtable.Name, nil)
	err := CreateMqtoDB(mqtable)
	if err != nil {
		*res = "Mq create failed!"
	} else {
		*res = mqtable.Name + " create success."
		log.Log("info", mqtable.Name+" created.", nil)
	}
	return nil
}
示例#14
0
文件: user.go 项目: jmptrader/Caesar
//user login control
func (users *Users) Login(user *User, res *string) error {
	//log.Log("warn","1234455566666666" , nil)
	if users.isLogined(user) {
		*res = "User has logined."
		return nil
	} else {
		i, err := db.VerifyUser(user.Name, user.Password)
		if err != nil {
			log.Log("err", err.Error(), nil)
			*res = "Server error."
			return nil
		} else {
			switch i {
			case 0: //no user
				*res = "Wrong username."
				return nil
			case 1:
				users.UM[user.Name] = *user
				err = db.UpdateRegisterTime(user.Name)
				if err != nil {
					log.Log("err", err.Error(), nil)
				}

				//fmt.Println(user.Key)
				*res = "Login success."

				//fmt.Printf("%s login.\n", user.Name)
				logs := "A new client login. Client's name is " + user.Name
				log.Log("info", logs, nil)
				return nil
			case 2: //wrong password
				*res = "Wrong password."
				return nil
			default:
				*res = "Server error."
				return nil
			}
		}
	}
}
示例#15
0
文件: flag.go 项目: jmptrader/Caesar
func FlagResolve(listenAddr *string) {
	flag.Parse()

	if *versionFlag != false {
		err := config.ReadConfigFile("../config/server.yml")
		if err != nil {
			fmt.Println(err)
			os.Exit(0)
		}
		version, _ := config.GetString("version")
		update, _ := config.GetList("update")
		instruction, _ := config.GetString("instruction")
		fmt.Printf("CaeserClient version: %s\n", version)
		fmt.Printf("New speciality contrast to old version: \n")
		for k, v := range update {
			fmt.Printf("%d-- %s\n", k+1, v)
		}
		fmt.Printf("       %s\n", instruction)
		os.Exit(0)
	}
	if *runtimeFlag == "min" {
		log.Log("warn", "Start up with least CPU's resousces", log.Fields{"Occupy CPU Num": 1})
	}

	if *listenFlag != "" {
		fp, err := regexp.MatchString(ipPattern, *listenFlag)
		handleError(err)
		if !fp {
			//fmt.Printf("\"%s\" is not a valid address, please check it and try again!\n", *listenFlag)
			log.Log("fatal", "-address is not a valid address, please check it and try again!", map[string]interface{}{"address": *listenFlag})
			os.Exit(0)
		}
		*listenAddr = *listenFlag
		fmt.Println("--Notice: you have set a new listen address", *listenAddr)
	} else {
		//fmt.Println("--Didn't set the listen address. Server will start at default address.")
		log.Log("info", "Didn't set the listen address. Server will start at default address.", log.Fields{"Default Address": *listenAddr})
	}
}
示例#16
0
//test message queue , if mq is runing,reuturn true
func (mqagent MqAgent) TestMq(w rest.ResponseWriter, r *rest.Request) {
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("err", err.Error(), nil)
	}
	_, ok := mqagent[mqid]
	if ok {
		w.WriteJson(map[string]string{"1020": "mq is running"})
	} else {
		w.WriteJson(map[string]string{"1010": "mq not running"})
	}

}
示例#17
0
文件: cflag.go 项目: jmptrader/Caesar
func FlagResolve(localAddr *string, serverAddr *string, username *string, password *string) {
	flag.Parse()

	if *helpFlag != false {
		//log.Log("info", "", nil)
		fmt.Println("Usage:...........!!!!!")
		os.Exit(0)
	}
	if *versionFlag != false {
		err := config.ReadConfigFile("../client/config/version.yml")
		if err != nil {
			fmt.Println(err)
			os.Exit(0)
		}
		version, _ := config.GetString("version")
		update, _ := config.GetList("update")
		instruction, _ := config.GetString("instruction")
		fmt.Printf("CaeserClient version: %s\n", version)
		fmt.Printf("New speciality contrast to old version: \n")
		for k, v := range update {
			fmt.Printf("%d-- %s\n", k+1, v)
		}
		fmt.Printf("       %s\n", instruction)
		os.Exit(0)
	}
	if *localFlag != "" {
		*localAddr = *localFlag
		log.Log("info", "you set a new addres", log.Fields{"address": *localFlag})
		//fmt.Println("--Notice: you have set a new address", *localAddr)
	} else {
		//fmt.Println("--Didn't set the start port. Caesar will start at default port.")
		log.Log("info", "Didn't set the start port. Caesar will start at default port.", log.Fields{"default address": *localAddr})
	}
	if *serverFlag != "" {
		fp, err := regexp.MatchString(ipPattern, *serverFlag)
		handleError(err)
		if !fp {
			//fmt.Printf("\"%s\" is not a valid address, please check it and try again!\n", *serverFlag)
			warnMsg := *serverFlag + "is not a valid address, please check it and try again!"
			log.Log("warn", warnMsg, nil)
			os.Exit(0)
		}
		*serverAddr = *serverFlag
		log.Log("info", "You have set a new server address", log.Fields{"new address": *serverAddr})
		//fmt.Println("--Notice: you have set a new server address", *serverAddr)
	} else {
		log.Log("info", "Didn't set the server address.Caesar will connect the default address.", log.Fields{"new address": *serverAddr})
		//fmt.Println("--Didn't set the server address. Caesar will connect the default address.")
	}
	if *userFlag != "" && *passwordFlag != "" {
		*username = *userFlag
		*password = *passwordFlag
		fmt.Println(*username, *password)
	} else {
		//fmt.Println("--Anonymous login, can do nothing! Please login with exgist user or register a new user.")
		log.Log("info", "Anonymous login, can do nothing! Please login with exgist user or register a new user.", nil)
	}
}
示例#18
0
文件: db.go 项目: jmptrader/Caesar
func DeleteUserById(userId int) error {

	err1 := DB.Connect()
	defer DB.Close()
	if err1 != nil {
		log.Log("err", err1.Error(), nil)
		return err1
	}
	stmt, err := DB.Prepare(`delete from user where user_id=?`)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//stmt.Bind(mqid)
	//_, _, err = stmt.Exec(mqid)
	_, _, err = stmt.Exec(userId)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}

	return nil
}
示例#19
0
文件: db.go 项目: jmptrader/Caesar
//
//func CreateMQ(msgq *MsQueue) error {
//
//	err := DB.Connect()
//
//	stmt, err := DB.Prepare("insert into msgqueue values (?, ?, ?, ?, ?)")
//
//	handleErr(err)
//	stmt.Bind(msgq)
//	handleErr(err)
//	//err = getData(msgq)
//	_, err = stmt.Run() //msgq.MQid, msgq.MQname, msgq.MQType, msgq.Owner, msgq.Persistence
//	handleErr(err)
//	return nil
//}
func CreateMqtoDB(mqTable *Mq_Table) error {
	err1 := DB.Connect()
	defer DB.Close()
	if err1 != nil {
		log.Log("err", err1.Error(), nil)
		return err1
	}
	stmt, err := DB.Prepare("insert into msgqueue values (?, ?, ?, ?, ?)")
	handleErr(err)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	stmt.Bind(mqTable)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//err = getData(msgq)
	stmt.Bind(mqTable.Id, mqTable.Name, mqTable.Type, mqTable.User_Name, mqTable.Bool_Persist)
	_, err = stmt.Run() //msgq.MQid, msgq.MQname, msgq.MQType, msgq.Owner, msgq.Persistence
	handleErr(err)
	return nil
}
示例#20
0
func (mqagent MqAgent) GetMsg(w rest.ResponseWriter, r *rest.Request) {
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := mqagent[mqid]
	if ok {
		mq.Lock()
		msg := mq.PopMsg()
		mq.Unlock()
		w.WriteJson(msg.Value)
	} else { //msgqueue nonexist

	}
}
示例#21
0
文件: db.go 项目: jmptrader/Caesar
func CreateMsgtoDB(msg *message.Message) error {
	err1 := DB.Connect()
	defer DB.Close()
	if err1 != nil {
		log.Log("err", err1.Error(), nil)
		return err1
	}
	stmt, err := DB.Prepare("insert into message values (?, ?, ?, ?, ?,?,?,?,?)")
	handleErr(err)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//stmt.Bind(mqTable)
	if err != nil {
		log.Log("err", err.Error(), nil)
		return err
	}
	//err = getData(msgq)
	stmt.Bind(msg.MsgId, msg.Value, msg.CreatedTime, msg.Generator, msg.EXP, msg.MsgType, msg.SubNum, msg.MQid, nil)
	_, err = stmt.Run() //msgq.MQid, msgq.MQname, msgq.MQType, msgq.Owner, msgq.Persistence
	handleErr(err)
	return nil
}
示例#22
0
文件: table.go 项目: jmptrader/Caesar
func (lead *User_Table) DeleteUser(arr []int, res *string) error {
	success := ""
	for _, v := range arr {
		err := DeleteUserById(v)
		if err != nil {
			*res = "Delete failed."
			return err
		} else {
			success += strconv.Itoa(v) + " "
			log.Log("info", strconv.Itoa(v)+" delete.", nil)
			DeleteMqByUserId(v)
		}
	}
	*res = success + "delete success."
	return nil
}
示例#23
0
func (mqagent MqAgent) PostMsg(w rest.ResponseWriter, r *rest.Request) {
	msg := message.NewMsg()
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := mqagent[mqid]
	if ok {
		msg.MQid = mqid
		r.DecodeJsonPayload(msg.Value)
		mq.Lock()
		mq.AddMsg(*msg)
		mq.Unlock()
		w.WriteJson(msg.Value)
	} else {

	}
}
示例#24
0
文件: table.go 项目: jmptrader/Caesar
func (lead *Mq_Table) DeleteMq(arr []int, res *string) error {
	//fmt.Println("here")
	//log.Log("info", mqtable.Name, nil)
	//	failed := ""
	success := ""
	for _, v := range arr {
		err := DeleteMqById(v)
		if err != nil {
			*res = "Delete failed."
			return err
		} else {
			success += strconv.Itoa(v) + " "
			log.Log("info", strconv.Itoa(v)+" delete.", nil)
		}
	}

	*res = success + "delete success."
	return nil
}
示例#25
0
文件: mqMsg.go 项目: jmptrader/Caesar
func (mqmsg MqMsg) GetMsg(w rest.ResponseWriter, r *rest.Request) {
	//msg := message.NewMsg()
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := DefaultMM[mqid]
	if ok {
		//fmt.Println("post msg")
		mq.Lock()
		msg := mq.PopMsg()
		mq.Unlock()
		if msg == nil {
			w.WriteJson(map[string]string{"1015": "no message in mq"})
		} else {
			//w.WriteJson(map[string]string{"1016": "post success"})
			w.WriteJson(msg)
		}
	} else {
		w.WriteJson(map[string]string{"1010": "mq not running"})
	}
}
示例#26
0
func (perAgent PerMqAgent) PostMsg(w rest.ResponseWriter, r *rest.Request) {
	//msg := message.NewMsg()
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := msgqueue.DefaultMM[mqid]
	if ok {
		perAgent.Msg.MQid = mqid
		perAgent.Msg.Generator = mq.Owner
		perAgent.Msg.MsgId = Counter
		Counter++
		r.DecodeJsonPayload(perAgent.Msg)

		//fmt.Println(mqmsg.Msg.Value)

		//fmt.Println("post msg")
		mq.Lock()
		//mq.AddMsg(*mqmsg.Msg)
		err = db.PushMsgToDB(perAgent.Msg)
		mq.Unlock()
		if err != nil {
			w.WriteJson(map[string]string{"1011": "server receive, but not save to db"})
			return
		}
		//w.WriteJson(perAgent.Msg)
		w.WriteJson(map[string]string{"1016": "post success"})
		//flag, err := db.GetMsgFlag(perAgent.Msg.MsgId)
		//		if flag == 0 {
		//			log.Log("err", err.Error(), nil)
		//		}else {
		//			Flag = flag
		//		}
		fmt.Println(Flag)
	} else {
		w.WriteJson(map[string]string{"1010": "mq not running"})
	}
}
示例#27
0
func (perAgent PerMqAgent) GetMsg(w rest.ResponseWriter, r *rest.Request) {
	//msg := message.NewMsg()
	mqid, err := strconv.Atoi(r.PathParam("mqid"))
	if err != nil {
		log.Log("info", err.Error(), nil)
	}
	mq, ok := msgqueue.DefaultMM[mqid]
	if ok {
		//fmt.Println("post msg")
		mq.Lock()
		msg, _ := db.PopMsgFromDBByFlag(Flag)
		mq.Unlock()
		if msg == nil {
			w.WriteJson(map[string]string{"1011": "no message in db"})
		} else {
			w.WriteJson(msg)
			Flag++
			//w.WriteJson(map[string]string{"1001": "get success"})
		}
	} else {
		w.WriteJson(map[string]string{"1010": "mq not running"})
	}
}
示例#28
0
文件: db.go 项目: jmptrader/Caesar
func GetMsgByFlag(flag int) (*message.Message, error) {
	err := DB.Connect()
	defer DB.Close()
	handleErr(err)
	rows, _, err := DB.Query("select * from message where flag='%d'", flag)

	if l := len(rows); l == 0 {
		return nil, errors.New("message not exist.")
	} else {
		//return NewMsgQue(id int,name string, mqType int, owner string, per bool), nil
		msg := message.Message{
			MsgId: rows[0].Int(0),
			Value: rows[0].Str(1),
			//CreatedTime:rows[0].Time(2,t),
			Generator: rows[0].Str(3),
			//EXP:time.Hour ,
			MsgType: rows[0].Str(5),
			SubNum:  rows[0].Int(6),
			MQid:    rows[0].Int(7),
		}
		log.Log("info", "msg", nil)
		return &msg, nil
	}
}
示例#29
0
文件: cflag.go 项目: jmptrader/Caesar
func handleError(err error) {
	if err != nil {
		//fmt.Print(err)
		log.Log("err", err.Error(), nil)
	}
}
示例#30
0
//resolve and excutive the command "cmd"
func disCmd(cmd string) bool {
	switch cmd {
	case "exit":
		fmt.Println("Caesar exit! Bye!")
		logOff()
		os.Exit(0)
		return true
	case "info":
		if DefautStatus.Login == true {
			log.Log("info", "You are online.", log.Fields{"Login Name": DefautStatus.LoginName})
		} else {
			log.Log("info", "You are offline.", nil)
		}
		return true
	case "test":
		var s string
		user := &command.User{"vvvvvvdfdf", "1111111"}
		rpcClient.Call("Test.Login", user, &s)
		fmt.Println(s)
		return true
	case "login":
		if DefautStatus.Login == true {
			log.Log("info", "You are already online.", log.Fields{"Login Name": DefautStatus.LoginName})
		} else {
			//input name and password
			fmt.Println("Please input name:")
			line, _, err := r.ReadLine()
			handleError(err)
			username := string(line)
			fmt.Println("Please input password:"******"regular", "unknow",genKey()}
			Me.Name = username
			Me.Password = password

			//fmt.Println(Me)
			rpcClient.Call("Users.Login", Me, &res)
			if res == "Login success." {
				StatusLine = 1
			}
			//fmt.Println(Me.Id)
			//fmt.Println(Me.Key)
			log.Log("info", res, nil)
		}
		return true
	case "myqueue":
		var simRes object.SimResult
		rpcClient.Call("Users.MyMQ", Me, &simRes)
		if simRes.LogInfo != "" {
			fmt.Print(simRes.LogInfo)
		} else {
			fmt.Printf(simRes.Res)
		}
		return true
	case "users":
		if Me.Name != "admin" {
			log.Log("err", "You are not administrator, no right!", nil)
			return true
		}
		var simRes object.SimResult
		rpcClient.Call("Users.Users", Me, &simRes)
		if simRes.LogInfo != "" {
			fmt.Print(simRes.LogInfo)
		} else {
			fmt.Printf(simRes.Res)
		}
		return true
	case "newqueue":
		fmt.Println("Please input name:")
		return true

	default:
		fmt.Printf("Command \"%s\" not found!\n", cmd)
		return false
	}
}