Развертывание программного обеспечения может включать в себя:
- Компиляцию кода, если это требуются;
- Выгрузку кода, или пакетов, в которых он содержится;
- Выгрузку зависимостей проекта, в том случае, если он опирается на сторонние библиотеки;
- Выполнение настроечных операций на сервере, каких-либо сценариев, которые необходимо выполнять каждый раз для конкретного проекта.
На сегодняшний день развертывание программного обеспечения принято производить максимально автоматизированным способом - то есть писать некоторые сценарии или использовать готовые инструменты, которые автоматизируют:
- Перенос кода проекта на сервер;
- Адаптацию проекта, например, установку базы данных или настройку проксирования;
- Непосредственно запуск на сервере. [10]
Созданный в ходе данной работы программный продукт может быть развернут на любом современном сервере, который поддерживает Java 8.
Перечислим общие действия для ручного развертывания разработанного приложения на сервере:
- Через файлы конфигураций проекта и сервера настраивается работа с базой данных. При этом предполагается, что необходимая база данных уже находится на сервере;
- С помощью инструмента для сборки, а именно Maven, проект собирается в JAR-пакет. Для этого используется консольная команда «mvn clean install»;
- Полученный в результате сборки пакет с приложением передается на удаленный сервер и запускается стандартной консольной командой «java -jar discasst.jar».
В итоге мы получаем полностью работоспособное приложение, доступ к которому можно получить через сеть интернет.
Заключение
Целью выпускной квалификационной работы стала разработка социальной подкаст-платформы. Для достижения цели был проведен аналитический обзор, в ходе которого были поставлены задачи, которые будет решать данный продукт, проведен анализ существующих аналогов разрабатываемой системы, определены типы пользователей и бизнес-правила. В ходе проектирования была создана use-case диаграмма, выбраны средства разработки, спроектирована схема базы данных, а также произведено проектирование пользовательского интерфейса.
Разработка платформы состояла из двух частей. Первой из них была разработка серверной части, в которой содержится бизнес-логика приложения и работа с базой данных. Вторым этапом стала разработка клиентской части, представляющей из себя веб-страницу.
Тестирование программного продукта состояло из нескольких этапов: модульного и нагрузочного тестирования. Также было оценено удобство использования пользовательского интерфейса приложения.
В результате проделанной работы программный продукт выполняет все возложенные на него задачи. Платформа сочетает в себе как функции, необходимые для удобного поиска и прослушивания подкастов, такие как поиск по жанрам, подписки и рейтинги, так и социальные функции, выраженные полнофункциональным комментированием выпусков и отдельными микроблогами подкастов.
Список использованных источников
1. iTunes Store: [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/ITunes_Store.
2. Spring Boot: [Электронный ресурс]. URL: http://spring-projects.ru/projects/spring-boot.
3. Spring Security: [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Spring_Security.
4. Шпаргалки Java программиста 10: Lombok: [Электронный ресурс]. URL: https://projectlombok.org.
5. React, или как перестать беспокоиться и начать жить: [Электронный ресурс]. URL: https://getinstance.info/articles/react/react-basics/.
6. Bootstrap (фреймворк): [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Bootstrap_(фреймворк).
7. Интерфейс пользователя: [Электронный ресурс]. URL: https://ru.bmstu.wiki/Интерфейс_пользователя.
8. JSON: [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/JSON.
9. Модульное тестирование: [Электронный ресурс]. URL: https://qalight.com.ua/baza-znaniy/modulnoe-testirovanie/.
10. Деплой -- что это в программировании (deploy): [Электронный ресурс]. URL: http://fkn.ktu10.com/?q=node/9501.
Приложение
Исходный код проекта
Исходный код серверной части проекта:
Файл: Discasst\src\main\resources\config\application.properties
server.port=3005
spring.data.rest.base-path=/service
logging.level.org.springframework.security=ERROR
Файл: Discasst\src\main\java\discasst\Application.java
package discasst;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Точка входа в приложение.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Файл: Discasst\src\main\java\discasst\config\SecurityConfig.java
package discasst.config;
import discasst.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import static org.springframework.http.HttpMethod.POST;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers(POST, "/getCoauthorsByIdList", "/getPodcastsInPage");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/podcast/**",
"/getCoauthors",
"/getPodcastById",
"/getEpisodesByPodcastIdInPage",
"/getEpisodesInFeedByUserIdInPage",
"/getPodcastsByUserIdInCoauthors",
"/user/**",
"/getAllGenres",
"/",
"/css/**",
"/generated/**",
"/images/**",
"/favicon.ico",
"/error",
"/register",
"/addUser",
"/getAllPodcasts",
"/getAllEpisodesByPodcastId",
"/getEpisodesCountToday",
"/getEpisodesCountThisMounth",
"/getPodcastsCountThisMounth",
"/getPodcastsCountToday",
"/getPodcastsByUserId/**",
"/getUsersCountThisMounth",
"/getUsersCountToday",
"/statistics",
"/getUserInfo",
"/getAllTwitsByPodcastId"
).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/signin")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/", true)
.failureUrl("/signin?error")
.permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
Файл: Discasst\src\main\java\discasst\da\CoauthorsRepository.java
package discasst.da;
import discasst.entity.CoauthorsEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface CoauthorsRepository extends CrudRepository<CoauthorsEntity, Long> {
List<CoauthorsEntity> findAllByPodcastId(long id);
@Query("SELECT a.podcastId FROM CoauthorsEntity a WHERE a.userId like:userId")
long[] findAllByUserId(@Param("userId") long userId);
void deleteAllByPodcastId(long id);
}
Файл: Discasst\src\main\java\discasst\da\EpisodeRepository.java
package discasst.da;
import discasst.entity.Episode;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface EpisodeRepository extends CrudRepository<Episode, Long> {
Page<Episode> findAllByPodcastIdOrderByIdDesc(long id, Pageable pageable);
Page<Episode> findAllByPodcastIdInOrderByIdDesc(long[] podcastIds, Pageable pageable);
List<Episode> findAllByDate(String date);
void deleteAllByPodcastId(long id);
@Query("select a from Episode a where a.date like:monthAndYear")
List<Episode> findAllInThisMonth(
@Param("monthAndYear") String monthAndYear);
}
Файл: Discasst\src\main\java\discasst\da\ExposeEntityIdRest.java
package discasst.da;
import discasst.entity.*;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.stereotype.Component;
@Component
public class ExposeEntityIdRest extends RepositoryRestConfigurerAdapter {
/**
* Сохраняет айдишники в объектах
*/
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(User.class);
config.exposeIdsFor(Podcast.class);
config.exposeIdsFor(Genre.class);
config.exposeIdsFor(Episode.class);
config.exposeIdsFor(CoauthorsEntity.class);
config.exposeIdsFor(RatingEntity.class);
config.exposeIdsFor(Tweet.class);
}
}
Файл: Discasst\src\main\java\discasst\da\GenreRepository.java
package discasst.da;
import discasst.entity.Genre;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface GenreRepository extends CrudRepository<Genre, Long> {
List<Genre> findAll();
}
Файл: Discasst\src\main\java\discasst\da\PodcastRepository.java
package discasst.da;
import discasst.entity.Podcast;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PodcastRepository extends PagingAndSortingRepository<Podcast, Long> {
Podcast findById(long id);
List<Podcast> findAll();
Page<Podcast> findByNameContainsIgnoreCaseAndGenreIdNotInOrderByRatingDesc(String name, long[] genres, Pageable pageable);
Page<Podcast> findByNameContainsIgnoreCaseAndGenreIdNotInOrderByIdDesc(String name, long[] genres, Pageable pageable);
List<Podcast> findAllByAuthorId(long id);
List<Podcast> findAllByIdIn(long[] ids);
List<Podcast> findAllByCreationDate(String date);
@Query("select a from Podcast a where a.creationDate like:monthAndYear")
List<Podcast> findAllInThisMonth(@Param("monthAndYear") String monthAndYear);
}
Файл: Discasst\src\main\java\discasst\da\RatingRepository.java
package discasst.da;
import discasst.entity.RatingEntity;
import org.springframework.data.repository.CrudRepository;
public interface RatingRepository extends CrudRepository<RatingEntity, Long> {
RatingEntity findByPodcastIdAndUserId(long podcastId, long userId);
void deleteAllByPodcastId(long id);
}
Файл: Discasst\src\main\java\discasst\da\SubscribeRepository.java
package discasst.da;
import discasst.entity.SubscribeEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface SubscribeRepository extends CrudRepository<SubscribeEntity, Long> {
SubscribeEntity findByPodcastIdAndUserId(long podcastId, long userId);
void deleteAllByPodcastId(long id);
@Query("SELECT a.podcastId FROM SubscribeEntity a WHERE a.userId like:userId AND a.isSubscribed LIKE true")
long[] findAllByUserId(@Param("userId") long userId);
}
Файл: Discasst\src\main\java\discasst\da\TweetRepository.java
package discasst.da;
import discasst.entity.Tweet;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface TweetRepository extends PagingAndSortingRepository<Tweet, Long> {
Page<Tweet> findAllByPodcastIdOrderByIdDesc(long id, Pageable pageable);
Page<Tweet> findAllByPodcastIdInOrderByIdDesc(long[] podcastIds, Pageable pageable);
void deleteAllByPodcastId(long id);
}
Файл: Discasst\src\main\java\discasst\da\UserRepository.java
package discasst.da;
import discasst.entity.User;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
List<User> findAllByIdIn(long[] list);
List<User> findAllByCreationDate(String date);
@Query("select a from User a where a.creationDate like:monthAndYear")
List<User> findAllInThisMonth(@Param("monthAndYear") String monthAndYear);
}
Файл: Discasst\src\main\java\discasst\entity\CoauthorsEntity.java
package discasst.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Data
public class CoauthorsEntity {
@Id
@GeneratedValue
private long id;
private long podcastId;
private long userId;
public CoauthorsEntity(long podcastId, long userId) {
this.podcastId = podcastId;
this.userId = userId;
}
}
Файл: Discasst\src\main\java\discasst\entity\Episode.java
package discasst.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.text.SimpleDateFormat;
import java.util.Date;
@Entity
@Data
public class Episode {
@Id
@GeneratedValue
private long id;
private long podcastId;
private String name;
@Column(columnDefinition = "TEXT")
private String description;
private String podsterLink;
private String date;
public long getPodcastId() {
return podcastId;
}
public Episode(long podcastId, String name,
String description, String podsterLink, Date date) {
this.podcastId = podcastId;
this.name = name;
this.description = description;
this.podsterLink = podsterLink;
this.date = new SimpleDateFormat("dd.MM.yy").format(date);
}
public void setDate(Date date) {
this.date = new SimpleDateFormat("dd.MM.yy").format(date);
}
}
Файл: Discasst\src\main\java\discasst\entity\Genre.java
package discasst.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Data
public class Genre {
@Id
@GeneratedValue
private long id;
@Column(unique = true)
private String name;
public Genre(String name) {
this.name = name;
}
}
Файл: Discasst\src\main\java\discasst\entity\Podcast.java
package discasst.entity;
import lombok.Data;
import javax.persistence.*;
import java.text.SimpleDateFormat;
import java.util.Date;
@Entity
@Data
@Table(name = "podcast")
public class Podcast {
@Id
@GeneratedValue
private long id;
@Column(unique = true)
private String name;
private long authorId;
@Column(columnDefinition="TEXT")
private String description;
private String itunesLink = "";
private String vkLink = "";
private String twitterLink = "";
private String facebookLink = "";
private Integer episodeCount = 0;
private Integer subscribersCount = 0;
private long genreId;
private Integer rating = 0;
private String creationDate;
public Podcast(String name, String description, Integer rating, Integer genreId, int episodeCount) {
this.name = name;
this.description = description;
this.rating = rating;
this.genreId = genreId;
this.episodeCount = episodeCount;
}
public Podcast(String name, String description, String itunesLink, String vkLink, String twitterLink,
String facebookLink, Integer episodeCount, long genreId, Integer rating, Integer subscribersCount, long authorId) {
this.name = name;
this.description = description;
this.itunesLink = itunesLink;
this.vkLink = vkLink;
this.twitterLink = twitterLink;
this.facebookLink = facebookLink;
this.episodeCount = episodeCount;
this.genreId = genreId;
this.rating = rating;
this.subscribersCount = subscribersCount;
this.authorId = authorId;
}
public Integer getEpisodeCount() {
return episodeCount;
}
public void setEpisodeCount(Integer episodeCount) {
this.episodeCount = episodeCount;
}
public void setCreationDate(Date date) {
this.creationDate = new SimpleDateFormat("dd.MM.yy").format(date);
}
public long getAuthorId() {
return authorId;
}
public void ratingInc() {
this.rating++;
}
public void ratingDec() {
this.rating--;
}
public void subscribersCountDec() {