2021SC@SDUSC
Application and practice of software engineering -- OpenMeetings project analysis (IV):
Undertake the task of last week and continue the source code analysis of various derived classes of BaseWebService under util package this week;
CalendarWebService.java
The first analysis is CalendarWebService.java. First paste the source code:
/** * CalendarService contains methods to create, edit delete calendar meetings * * @author sebawagner * */ @Service("calendarWebService") @WebService(serviceName="org.apache.openmeetings.webservice.CalendarWebService", targetNamespace = TNS) @Features(features = "org.apache.cxf.ext.logging.LoggingFeature") @Produces({MediaType.APPLICATION_JSON}) @Path("/calendar") public class CalendarWebService extends BaseWebService { private static final Logger log = Red5LoggerFactory.getLogger(CalendarWebService.class, getWebAppRootKey()); private static AppointmentDao getDao() { return getBean(AppointmentDao.class); } /** * Load appointments by a start / end range for the current SID * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param start * start time * @param end * end time * * @return - list of appointments in range */ @GET @Path("/{start}/{end}") public List<AppointmentDTO> range(@QueryParam("sid") @WebParam(name="sid") String sid , @PathParam("start") @WebParam(name="start") Calendar start , @PathParam("end") @WebParam(name="end") Calendar end ) { log.debug("range : startdate - {} , enddate - {}" , start == null ? "" : start.getTime() , end == null ? "" : end.getTime()); return performCall(sid, User.Right.Room , sd -> AppointmentDTO.list(getDao().getInRange(sd.getUserId(), start.getTime(), end.getTime()))); } /** * Load appointments by a start / end range for the userId * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param userid * the userId the calendar events should be loaded * @param start * start time * @param end * end time * * @return - list of appointments in range */ @GET @Path("/{userid}/{start}/{end}") public List<AppointmentDTO> rangeForUser( @QueryParam("sid") @WebParam(name="sid") String sid , @PathParam("userid") @WebParam(name="userid") long userid , @PathParam("start") @WebParam(name="start") Calendar start , @PathParam("end") @WebParam(name="end") Calendar end ) { log.debug("rangeForUser : startdate - {} , enddate - {}" , start == null ? "" : start.getTime() , end == null ? "" : end.getTime()); return performCall(sid, User.Right.Soap , sd -> AppointmentDTO.list(getDao().getInRange(userid, start.getTime(), end.getTime()))); } /** * Get the next Calendar event for the current user of the SID * * @param sid * The SID of the User. This SID must be marked as Loggedin * @return - next Calendar event */ @GET @Path("/next") public AppointmentDTO next(@QueryParam("sid") @WebParam(name="sid") String sid) { return performCall(sid, User.Right.Room, sd -> { Appointment a = getDao().getNext(sd.getUserId(), new Date()); return a == null ? null : new AppointmentDTO(a); }); } /** * Get the next Calendar event for userId * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param userid * the userId the calendar events should be loaded * * @return - next Calendar event */ @GET @Path("/next/{userid}") public AppointmentDTO nextForUser(@QueryParam("sid") @WebParam(name="sid") String sid, @PathParam("userid") @WebParam(name="userid") long userid) { return performCall(sid, User.Right.Soap, sd -> { Appointment a = getDao().getNext(userid, new Date()); return a == null ? null : new AppointmentDTO(a); }); } /** * * Load a calendar event by its room id * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param roomid * id of appointment special room * @return - calendar event by its room id */ @GET @Path("/room/{roomid}") public AppointmentDTO getByRoom(@QueryParam("sid") @WebParam(name="sid") String sid, @PathParam("roomid") @WebParam(name="roomid") long roomid) { return performCall(sid, User.Right.Room, sd -> { Appointment a = getDao().getByRoom(sd.getUserId(), roomid); return a == null ? null : new AppointmentDTO(a); }); } /** * Search a calendar event for the current SID * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param title * the search string * * @return - calendar event list */ @GET @Path("/title/{title}") public List<AppointmentDTO> getByTitle(@QueryParam("sid") @WebParam(name="sid") String sid, @PathParam("title") @WebParam(name="title") String title) { return performCall(sid, User.Right.Room, sd -> AppointmentDTO.list(getDao().searchByTitle(sd.getUserId(), title))); } /** * Save an appointment * * @param sid * The SID of the User. This SID must be marked as Loggedin * @param appointment * calendar event * * @return - appointment saved */ @WebMethod @POST @Path("/") public AppointmentDTO save(@QueryParam("sid") @WebParam(name="sid") String sid, @FormParam("appointment") @WebParam(name="appointment") AppointmentDTO appointment) { //Seems to be create log.debug("save SID: {}", sid); UserDao userDao = getUserDao(); return performCall(sid, sd -> { User u = userDao.get(sd.getUserId()); if (!AuthLevelUtil.hasUserLevel(u.getRights())) { log.error("save: not authorized"); return false; } return AuthLevelUtil.hasWebServiceLevel(u.getRights()) || appointment.getOwner() == null || appointment.getOwner().getId().equals(u.getId()); }, sd -> { User u = userDao.get(sd.getUserId()); AppointmentDao dao = getDao(); Appointment a = appointment.get(userDao, getFileDao(), dao, u); if (a.getRoom().getId() != null) { if (a.getRoom().isAppointment()) { a.getRoom().setIspublic(false); } else { a.setRoom(getRoomDao().get(a.getRoom().getId())); } } return new AppointmentDTO(dao.update(a, u.getId())); }); } /** * * delete a calendar event * * If the given sid is from an Administrator or Web-Service user, the user * can delete any appointment. * If the sid is assigned to a regular user, he can only delete appointments * where he is also the owner/creator of the appointment * * @param sid * an authenticated SID * @param id * the id to delete * @return {@link ServiceResult} with result type */ @DELETE @Path("/{id}") public ServiceResult delete(@QueryParam("sid") @WebParam(name="sid") String sid, @PathParam("id") @WebParam(name="id") Long id) { AppointmentDao dao = getDao(); Appointment a = dao.get(id); return performCall(sid, sd -> { Set<Right> rights = getRights(sd.getUserId()); if (AuthLevelUtil.hasWebServiceLevel(rights) || AuthLevelUtil.hasAdminLevel(rights)) { return true; // fine } if (AuthLevelUtil.hasUserLevel(rights) && a.getOwner().getId().equals(sd.getUserId())) { return true; } return false; }, sd -> { if (a == null) { throw new ServiceException("Bad id"); } dao.delete(a, sd.getUserId()); return new ServiceResult("Deleted", Type.SUCCESS); }); } }
It can be seen from the comments that the CalendarWebService class contains methods for creating, editing and deleting meetings on specified dates. Therefore, its main function is to add, delete and edit dates when booking meetings; First observe the structure diagram of this class generated by IDEA, as follows:
It includes several public methods, a private method, and a private data log;
getDao method: return the appointment Dao (an entity class under db.dao.caller), call the getBean method of its parent class, and pass in the class attribute of the appointment Dao to return a bean;
Range method: according to the annotation of the method, this method needs three parameters, SID, start, end, returns a list of AppointmentDTo with a specified range. First, it uses org.slf4j.Logger's log object to call debug method to start a date and end date to make a non empty judgement, then calls the parent class's performcall method to return a list.
rangeForuser method: four parameters are required: the user's sid (the user must be logged in), userid, numeric user identifier, start time and end time. Similarly, a list of appointment DTOs is returned; Both methods use a getInrange method of the appointment class. Therefore, this method is also analyzed. It also requires user id, start time and end time. Create a queue using the javax.persistence.EntityManager object, and then create the appointment object according to the parameter requirements to add it to a new list. Finally, return the list;
Next method: get the next date event of the user with user id sid, receive a sid parameter, and return a calendar event object, that is, the appointment dto type;
nextForUser method: the parameters are sid and userid, which are respectively the id of the currently logged in user and the id of the person to load the event. The same is the next Calendar event, the next date event
getByRoom method: this method is used to load date events by room ID, the receiving parameter is sid, roomid is the room label of the meeting, and returns an appointment dto;
getByTitle method: search the calendar events of the current user (user id is sid), receive the parameters sid and title (search title), and return a list of appointment dto types;
Save method: see the meaning of the name and save. This method is used to save calendar events. The receiving parameters are sid and appointment object (appointment dto), and the return value is appointment dto;
At the same time, lambda expressions and functional interfaces predict and Function are used in the method. It was mentioned in the analysis last week. For example, the two parameters SD - > {} in performcall in the method are lambda expressions. For example, consumer is a functional interface:
Consumer<String> consumer = s -> System.out.println(s); consumer.accept("Java3y");
Delete sender: the same as above. The delete method is used to delete calendar events. If the given Sid comes from an administrator or Web service user, the user can delete any appointment. If sid is assigned to an ordinary user, he can only delete the appointment that is also the owner / creator of the appointment. The receiving parameters are the authenticated user Sid and the conference id to be deleted, and a ServiceResult with result type is returned;
Constants.java
Constants means constants, so this class mainly defines some constants;
Paste the source code first:
package org.apache.openmeetings.webservice; public class Constants { public static final String TNS = "http://webservice.openmeetings.apache.org/"; public static final String USER_SERVICE_NAME = "org.apache.openmeetings.webservice.UserWebService"; public static final String USER_SERVICE_PORT_NAME = "UserService"; private Constants() {} }
Defines three constants of static string type TNS\USER_SERVICE_NAME and USER_SERVICE_PORT_NAME
ErrorWebService.java
Attach the source code first:
package org.apache.openmeetings.webservice; import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey; import static org.apache.openmeetings.webservice.Constants.TNS; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.apache.cxf.feature.Features; import org.apache.openmeetings.db.dao.label.LabelDao; import org.apache.openmeetings.db.dto.basic.ServiceResult; import org.apache.openmeetings.db.dto.basic.ServiceResult.Type; import org.apache.openmeetings.db.entity.server.Sessiondata; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; import org.springframework.stereotype.Service; /** * * The Service contains methods to get localized errors * * @author solomax * */ @Service("errorWebService") @WebService(serviceName="org.apache.openmeetings.webservice.ErrorWebService", targetNamespace = TNS) @Features(features = "org.apache.cxf.ext.logging.LoggingFeature") @Produces({MediaType.APPLICATION_JSON}) @Path("/error") public class ErrorWebService extends BaseWebService { private static final Logger log = Red5LoggerFactory.getLogger(ErrorWebService.class, getWebAppRootKey()); /** * loads an Error-Object. If a Method returns a negative Result, its an * Error-id, it needs a languageId to specify in which language you want to * display/read the error-message. English has the Language-ID one, for * different one see the list of languages * * @param key * the error key for ex. `error.unknown` * @param lang * The id of the language * * @return - error with the code given */ @WebMethod @GET @Path("/{key}/{lang}") public ServiceResult get(@WebParam(name="key") @PathParam("key") String key, @WebParam(name="lang") @PathParam("lang") long lang) { try { String eValue = LabelDao.getString(key, lang); return new ServiceResult(eValue, Type.SUCCESS); } catch (Exception err) { log.error("[get] ", err); } return null; } @WebMethod @POST @Path("/report/") public void report(@WebParam(name="sid") @QueryParam("sid") String sid, @WebParam(name="message") @QueryParam("message") String message) { if (sid != null && message != null) { Sessiondata sd = check(sid); if (sd.getId() != null) { log.error("[CLIENT MESSAGE] " + message); } } } }
ErrorWebService class inherits from basewebservice and contains methods to obtain localization errors; The amount of code is small. Observe its structure from idea, as shown in the figure:
It contains a member variable. Like the previous classes, log uses the factory mode. Log is produced by the getLogger method called by the Red5LoggerFactory object; It also includes two common methods, get and report;
get method: load an error object. If a method returns a negative result, it is an error id. it needs a languageId to specify which language you want to display / read the error message in. English has one of the language IDS, and different languages can be specified according to the language list; The received parameter is key, the wrong key, and lang is the id identifier of the language; Return a code error of ServiceResult type;
report method: detect whether there is an exception. If an exception is detected, call the error method of the log object to send the exception message;
FileWebService.java
package org.apache.openmeetings.webservice; @Service("fileWebService") @WebService(serviceName="org.apache.openmeetings.webservice.FileWebService", targetNamespace = TNS) @Features(features = "org.apache.cxf.ext.logging.LoggingFeature") @Produces({MediaType.APPLICATION_JSON}) @Path("/file") public class FileWebService extends BaseWebService { private static final Logger log = Red5LoggerFactory.getLogger(FileWebService.class, getWebAppRootKey()); /** * deletes files or folders based on it id * * @param sid * The SID of the User. This SID must be marked as logged in * @param id * the id of the file or folder * @return {@link ServiceResult} with result type */ @DELETE @Path("/{id}") public ServiceResult delete(@QueryParam("sid") @WebParam(name="sid") String sid, @PathParam("id") @WebParam(name="id") Long id) { FileItemDao dao = getFileDao(); FileItem f = dao.get(id); return performCall(sid, sd -> { Long userId = sd.getUserId(); Set<Right> rights = getRights(userId); return AuthLevelUtil.hasWebServiceLevel(rights) || (AuthLevelUtil.hasUserLevel(rights) && userId.equals(f.getOwnerId())); } , sd -> { if (f == null) { return new ServiceResult("Bad id", Type.ERROR); } dao.delete(f); return new ServiceResult("Deleted", Type.SUCCESS); }); } /** * * deletes a file by its external Id and type * * @param sid * The SID of the User. This SID must be marked as logged in * @param externalId * the od of the file or folder * @param externalType * the externalType * @return {@link ServiceResult} with result type */ @DELETE @Path("/{externaltype}/{externalid}") public ServiceResult deleteExternal( @WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="externaltype") @PathParam("externaltype") String externalType , @WebParam(name="externalid") @PathParam("externalid") String externalId ) { return performCall(sid, User.Right.Soap, sd -> { FileItemDao dao = getFileDao(); FileItem f = dao.get(externalId, externalType); dao.delete(f); return new ServiceResult("Deleted", Type.SUCCESS); }); } /** * to add a folder to the private drive, set parentId = 0 and isOwner to 1/true and * externalUserId/externalUserType to a valid user * * @param sid * The SID of the User. This SID must be marked as logged in * @param file * the The file to be added * @param stream * the The file to be added * @return - Object created */ @WebMethod @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("/") public FileItemDTO add(@WebParam(name="sid") @QueryParam("sid") String sid , @Multipart(value = "file", type = MediaType.APPLICATION_JSON) @WebParam(name="file") FileItemDTO file , @Multipart(value = "stream", type = MediaType.APPLICATION_OCTET_STREAM, required = false) @WebParam(name="stream") InputStream stream ) { return performCall(sid, User.Right.Soap, sd -> { FileItem f = file == null ? null : file.get(); if (f == null || f.getId() != null) { throw new ServiceException("Bad id"); } f.setInsertedBy(sd.getUserId()); if (stream != null) { try { ProcessResultList result = getBean(FileProcessor.class).processFile(f, stream); if (result.hasError()) { throw new ServiceException(result.getLogMessage()); } } catch (Exception e) { throw new ServiceException(e.getMessage()); } } else { f = getFileDao().update(f); } return new FileItemDTO(f); }); } /** * Get all files by external type * * @param sid * The SID of the User. This SID must be marked as logged in * @param externalType * External type for file listing * @return - the list of file for given external type */ @WebMethod @GET @Path("/{externaltype}") public List<FileItemDTO> getAllExternal(@WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="externaltype") @PathParam("externaltype") String externalType ) { log.debug("getAllExternal::externalType {}", externalType); return performCall(sid, User.Right.Soap, sd -> { FileItemDao dao = getFileDao(); return FileItemDTO.list(dao.getExternal(externalType)); }); } /** * Get a File Explorer Object by a given Room * * @param sid * The SID of the User. This SID must be marked as logged in * @param roomId * Room Id * @return - File Explorer Object by a given Room */ @WebMethod @GET @Path("/room/{id}") public FileExplorerObject getRoom(@WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="id") @PathParam("id") long roomId ) { log.debug("getRoom::roomId {}", roomId); return performCall(sid, User.Right.Soap, sd -> { FileItemDao dao = getFileDao(); FileExplorerObject fileExplorerObject = new FileExplorerObject(); // Home File List List<FileItem> fList = dao.getByOwner(sd.getUserId()); fileExplorerObject.setUser(fList, dao.getSize(fList)); // Public File List List<FileItem> rList = dao.getByRoom(roomId); fileExplorerObject.setRoom(rList, dao.getSize(rList)); return fileExplorerObject; }); } /** * * Get list of {@link FileItemDTO} by parent * * @param sid * SID The SID of the User. This SID must be marked as logged in * @param parentId * the parent folder id * @param roomId * the room id * @return - list of file explorer items */ @WebMethod @GET @Path("/room/{id}/{parent}") public List<FileItemDTO> getRoomByParent(@WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="id") @PathParam("id") long roomId , @WebParam(name="parent") @PathParam("parent") long parentId ) { log.debug("getRoomByParent {}", parentId); return performCall(sid, User.Right.Room, sd -> { FileItemDao dao = getFileDao(); List<FileItem> list; if (parentId < 0) { if (parentId == -1) { list = dao.getByOwner(sd.getUserId()); } else { list = dao.getByRoom(roomId); } } else { list = dao.getByParent(parentId); } return FileItemDTO.list(list); }); } /** * * update a file or folder name * * @param sid * SID The SID of the User. This SID must be marked as logged in * @param id * file or folder id * @param name * new file or folder name * @return - resulting file object */ @WebMethod @POST @Path("/rename/{id}/{name}") public FileItemDTO rename(@WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="id") @PathParam("id") long id , @WebParam(name="name") @PathParam("name") String name) { log.debug("rename {}", id); return performCall(sid, User.Right.Soap, sd -> { FileItem f = getFileDao().rename(id, name); return f == null ? null : new FileItemDTO(f); }); } /** * move a file or folder * * @param sid * SID The SID of the User. This SID must be marked as logged in * @param id * current file or folder id to be moved * @param roomId * room this file need to be moved * @param parentId * new parent folder id * @return - resulting file object */ @WebMethod @POST @Path("/move/{roomid}/{id}/{parentid}") public FileItemDTO move(@WebParam(name="sid") @QueryParam("sid") String sid , @WebParam(name="id") @PathParam("id") long id , @WebParam(name="roomid") @PathParam("roomid") long roomId , @WebParam(name="parentid") @PathParam("parentid") long parentId) { log.debug("move {}", id); return performCall(sid, User.Right.Soap, sd -> { FileItem f = getFileDao().move(id, parentId, sd.getUserId(), roomId); return f == null ? null : new FileItemDTO(f); }); } }
As above, first view the structure of the class:
FileWebService.java inherits from basewebservice and contains methods to import and upload files to the file part of the conference room and any user's personal drive; It mainly includes 8 public methods and one member variable;
Delete method: delete the uploaded file or folder according to the id, the receiving parameters are the id of the sid login user and the id of the file to be deleted, and the returned result type is ServiceResult;
deleteExternal method: delete files by external ID and type. The receiving parameters are: SID - user SID. This SID must be marked as logged in, externalType - external type, externalId - od of file or folder, and the return value is ServiceResult with result type
Add method: the main purpose of this method is to add a file to the meeting. To add a folder to a private drive, set parentId = 0 and isOwner to 1/true, and set externalUserId/externalUserType to a valid user. The receiving parameter is SID - the user's SID. This SID must be marked as logged in, and file - the file to be added, Stream – the file (stream type) to be added, and a FileItemDTO is returned;
getAllExternal method: get all files by external type, and receive formal parameter: SID - user's SID. This SID must be marked as logged in. externalType - external type of the file list. Return value: file list of the given external type ';
getRoom method: the main purpose of this method is to obtain the file resource manager object through a given room and receive the formal parameter: SID - the SID of the user. This SID must be marked as logged in, roomId – room ID, return value: File Explorer object for the given room
getRoomByParent method: the main purpose of this method is to obtain the FileItemDTO list through the parent and receive the formal parameter: SID - SID of the user. This SID must be marked as logged in, roomId – room ID, parentId – parent folder ID. return value: File Explorer item list
rename method: the main purpose of this method is to update the file or folder name and receive the formal parameter: SID - SID of the user. This SID must be marked as logged in, ID - file or folder ID, name - new file or folder name return value: result file object
Move method: the main purpose of this method is to move files or folders and receive the formal parameter: SID - SID of the user. This SID must be marked as logged in, ID - the ID of the current file or folder to be moved, roomId - the room where the file needs to be moved, parentId - the ID of the new parent folder, and the return value: the result file object
Summary: these webservice classes are inherited from basewebservice. They are used to control and manage different transactions and complete various operations by calling multiple dao objects injected; This week, we failed to analyze all the derived classes under the util package. Next week, we will analyze the remaining derived classes to find out what services each class is responsible for controlling and managing, and then start the analysis under the web package according to the situation.